Package: AstLocationSearcher

AstLocationSearcher

nameinstructionbranchcomplexitylinemethod
AstLocationSearcher()
M: 15 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
caseAFieldField(AFieldField)
M: 7 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
caseAFunctionType(AFunctionType)
M: 1 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
caseARecordInvariantType(ARecordInvariantType)
M: 8 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
caseAVariableExp(AVariableExp)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
check(INode, ILexLocation)
M: 214 C: 0
0%
M: 34 C: 0
0%
M: 18 C: 0
0%
M: 42 C: 0
0%
M: 1 C: 0
0%
createIndex(List, IVdmElement)
M: 30 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
defaultInPDefinition(PDefinition)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
defaultInPExp(PExp)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
defaultInPPattern(PPattern)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
defaultInPStm(PStm)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getNodeOffset(ILexLocation)
M: 18 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getNodeOffset(INode)
M: 98 C: 0
0%
M: 20 C: 0
0%
M: 11 C: 0
0%
M: 21 C: 0
0%
M: 1 C: 0
0%
init()
M: 19 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
search(List, int, IVdmSourceUnit)
M: 45 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 11 C: 0
0%
M: 1 C: 0
0%
searchCache(List, int, IVdmElement)
M: 67 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 14 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 11 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * #%~
3: * org.overture.ide.ui
4: * %%
5: * Copyright (C) 2008 - 2014 Overture
6: * %%
7: * This program is free software: you can redistribute it and/or modify
8: * it under the terms of the GNU General Public License as
9: * published by the Free Software Foundation, either version 3 of the
10: * License, or (at your option) any later version.
11: *
12: * This program is distributed in the hope that it will be useful,
13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15: * GNU General Public License for more details.
16: *
17: * You should have received a copy of the GNU General Public
18: * License along with this program. If not, see
19: * <http://www.gnu.org/licenses/gpl-3.0.html>.
20: * #~%
21: */
22: package org.overture.ide.ui.utility.ast;
23:
24: import java.io.File;
25: import java.util.HashMap;
26: import java.util.List;
27: import java.util.Map;
28: import java.util.Map.Entry;
29:
30: import org.overture.ast.analysis.AnalysisException;
31: import org.overture.ast.analysis.DepthFirstAnalysisAdaptor;
32: import org.overture.ast.definitions.ATypeDefinition;
33: import org.overture.ast.definitions.PDefinition;
34: import org.overture.ast.expressions.AVariableExp;
35: import org.overture.ast.expressions.PExp;
36: import org.overture.ast.intf.lex.ILexLocation;
37: import org.overture.ast.modules.AFromModuleImports;
38: import org.overture.ast.modules.AModuleImports;
39: import org.overture.ast.modules.AModuleModules;
40: import org.overture.ast.modules.PExport;
41: import org.overture.ast.modules.PImport;
42: import org.overture.ast.node.INode;
43: import org.overture.ast.patterns.PPattern;
44: import org.overture.ast.statements.PStm;
45: import org.overture.ast.types.AFieldField;
46: import org.overture.ast.types.AFunctionType;
47: import org.overture.ast.types.ARecordInvariantType;
48: import org.overture.ide.core.IVdmElement;
49: import org.overture.ide.core.resources.IVdmSourceUnit;
50: import org.overture.ide.ui.internal.viewsupport.ImportsContainer;
51:
52: /**
53: * Class used by an editor to search the editor text for source code node locations. Used to find nodes in the source
54: * code to sync with outline
55: *
56: * @author kela
57: */
58: public final class AstLocationSearcher extends DepthFirstAnalysisAdaptor
59: {
60:
61:         private static boolean DEBUG_PRINT = false;
62:
63:         /**
64:          * Best match to the offset. This means that this node has a location where the offset is within
65:          */
66:         private INode bestHit = null;
67:         /**
68:          * Best alternative hit is a location which is abs close to the offset
69:          */
70:         private INode bestAlternativeHit = null;
71:         /**
72:          * Best alternative hit is a node which has a location which is abs close to the offset
73:          */
74:         private ILexLocation bestAlternativeLocation;
75:         /**
76:          * The offset used when searching for nodes within this location of the source code
77:          */
78:         private int offSet;
79:
80:         /**
81:          * The source file to search
82:          */
83:         private File sourceFile;
84:
85:         private static final AstLocationSearcher seacher = new AstLocationSearcher();
86:
87:         private static final Map<IVdmElement, Map<ILexLocation, INode>> elementNodeCache = new HashMap<IVdmElement, Map<ILexLocation, INode>>();
88:
89:         private IVdmElement currentElement = null;
90:
91:         private boolean indexing = false;
92:
93:         /**
94:          * Private constructor, special care is needed to the state of the class this no instanciation allowed outside this
95:          * class
96:          */
97:         private AstLocationSearcher()
98:         {
99:         }
100:
101:         private void init()
102:         {
103:                 seacher._visitedNodes.clear();// We cheat with undeclared exception, this breaks the state of the adaptor, and
104:                                                                                 // we use
105:                 // static so we need to clear the cache.
106:                 seacher.bestHit = null;
107:                 seacher.bestAlternativeHit = null;
108:                 seacher.bestAlternativeLocation = null;
109:                 seacher.currentElement = null;
110:                 seacher.indexing = false;
111:         }
112:
113:         /**
114:          * Search method to find the closest node to a location specified by a test offset
115:          *
116:          * @param nodes
117:          * The nodes to search within
118:          * @param offSet
119:          * The offset to match a node to
120:          * @return The node closest to the offset or null
121:          */
122:         public static INode search(List<INode> nodes, int offSet,
123:                         IVdmSourceUnit source)
124:         {
125:                 synchronized (seacher)
126:                 {
127:•                        if (DEBUG_PRINT)
128:                         {
129:                                 System.out.println("Search start");
130:                         }
131:                         seacher.init();
132:                         seacher.offSet = offSet;
133:                         seacher.sourceFile = source.getSystemFile();
134:                         try
135:                         {
136:•                                for (INode node : nodes)
137:                                 {
138:                                         node.apply(seacher);
139:                                 }
140:                         } catch (AnalysisException e)
141:                         {
142:                                 // We found what we are looking for
143:                         }
144:
145:•                        return seacher.bestHit != null ? seacher.bestHit
146:                                         : seacher.bestAlternativeHit;
147:                 }
148:
149:         }
150:
151:         /**
152:          * Search method to find the closest node to a location specified by a test offset
153:          *
154:          * @param nodes
155:          * The nodes to search within
156:          * @param offSet
157:          * The offset to match a node to
158:          * @param element
159:          * @return The node closest to the offset or null
160:          */
161:         public static INode searchCache(List<INode> nodes, int offSet,
162:                         IVdmElement element)
163:         {
164:                 synchronized (seacher)
165:                 {
166:•                        if (DEBUG_PRINT)
167:                         {
168:                                 System.out.println("Search start");
169:                         }
170:                         seacher.init();
171:                         seacher.offSet = offSet;
172:                         seacher.currentElement = element;
173:                         try
174:                         {
175:•                                if (elementNodeCache.get(element) == null
176:•                                                || elementNodeCache.get(element).isEmpty())
177:                                 {
178:                                         // elementNodeCache.put(element, new HashMap<ILexLocation, INode>());
179:                                         // seacher.indexing = true;
180:                                         // for (INode node : nodes)
181:                                         // {
182:                                         // node.apply(seacher);
183:                                         // }
184:                                         return null;
185:                                 } else
186:                                 {
187:•                                        for (Entry<ILexLocation, INode> entry : elementNodeCache.get(element).entrySet())
188:                                         {
189:                                                 seacher.check(entry.getValue(), entry.getKey());
190:                                         }
191:                                 }
192:
193:                         } catch (AnalysisException e)
194:                         {
195:                                 // We found what we are looking for
196:                         }
197:
198:•                        return seacher.bestHit != null ? seacher.bestHit
199:                                         : seacher.bestAlternativeHit;
200:                 }
201:
202:         }
203:
204:         public static void createIndex(List<INode> nodes, IVdmElement element)
205:                         throws Throwable
206:         {
207:                 seacher.init();
208:                 seacher.currentElement = element;
209:                 elementNodeCache.put(element, new HashMap<ILexLocation, INode>());
210:                 seacher.indexing = true;
211:•                for (INode node : nodes)
212:                 {
213:                         node.apply(seacher);
214:                 }
215:         }
216:
217:         @Override
218:         public void defaultInPDefinition(PDefinition node) throws AnalysisException
219:         {
220:                 check(node, node.getLocation());
221:         }
222:
223:         @Override
224:         public void defaultInPExp(PExp node) throws AnalysisException
225:         {
226:                 check(node, node.getLocation());
227:         }
228:
229:         @Override
230:         public void defaultInPStm(PStm node) throws AnalysisException
231:         {
232:                 check(node, node.getLocation());
233:         }
234:
235:         @Override
236:         public void caseAVariableExp(AVariableExp node) throws AnalysisException
237:         {
238:                 check(node, node.getLocation());
239:         }
240:
241:         @Override
242:         public void caseAFieldField(AFieldField node) throws AnalysisException
243:         {
244:                 check(node, node.getTagname().getLocation());
245:         }
246:
247:         @Override
248:         public void defaultInPPattern(PPattern node) throws AnalysisException
249:         {
250:                 check(node, node.getLocation());
251:         }
252:
253:         @Override
254:         public void caseAFunctionType(AFunctionType node)
255:         {
256:                 // Skip
257:         }
258:
259:         @Override
260:         public void caseARecordInvariantType(ARecordInvariantType node)
261:                         throws AnalysisException
262:         {
263:•                if (node.parent() instanceof ATypeDefinition)
264:                 {
265:                         super.caseARecordInvariantType(node);
266:                 }
267:                 // Skip
268:         }
269:
270:         private void check(INode node, ILexLocation location)
271:                         throws AnalysisException
272:         {
273:•                if (DEBUG_PRINT)
274:                 {
275:                         System.out.println("Checking location span " + offSet + ": "
276:                                         + location.getStartOffset() + " to "
277:                                         + location.getEndOffset() + " line: "
278:                                         + location.getStartLine() + ":" + location.getStartPos());
279:                 }
280:•                if (currentElement != null)
281:                 {
282:                         elementNodeCache.get(currentElement).put(location, node);
283:                 }
284:•                if (location.getStartOffset() - 1 <= this.offSet
285:•                                && location.getEndOffset() - 1 >= this.offSet
286:•                                && location.getFile().equals(sourceFile))
287:                 {
288:                         bestHit = node;
289:•                        if (!indexing)
290:                         {
291:                                 throw new AnalysisException("Hit found stop search");
292:                         }
293:                 }
294:
295:                 // Store the last best match where best is closest with abs
296:•                if (bestAlternativeLocation == null
297:•                                || Math.abs(offSet - location.getStartOffset()) <= Math.abs(offSet
298:                                                 - bestAlternativeLocation.getStartOffset())
299:•                                && location.getFile().equals(sourceFile))
300:                 {
301:                         bestAlternativeLocation = location;
302:                         bestAlternativeHit = node;
303:•                        if (DEBUG_PRINT)
304:                         {
305:                                 System.out.println("Now best is: " + offSet + ": "
306:                                                 + location.getStartOffset() + " to "
307:                                                 + location.getEndOffset() + " line: "
308:                                                 + location.getStartLine() + ":"
309:                                                 + location.getStartPos());
310:                         }
311:•                } else if (bestAlternativeLocation == null
312:•                                || offSet - bestAlternativeLocation.getStartOffset() > 0
313:•                                && Math.abs(offSet - location.getStartOffset()) > Math.abs(offSet
314:                                                 - bestAlternativeLocation.getStartOffset())
315:•                                && location.getFile().equals(sourceFile))
316:                 {
317:•                        if (DEBUG_PRINT)
318:                         {
319:                                 System.out.println("Going back...");
320:                         }
321:                 } else
322:                 {
323:•                        if (DEBUG_PRINT)
324:                         {
325:                                 System.out.println("Rejected is: " + offSet + ": "
326:                                                 + location.getStartOffset() + " to "
327:                                                 + location.getEndOffset() + " line: "
328:                                                 + location.getStartLine() + ":"
329:                                                 + location.getStartPos());
330:                         }
331:•                        if (!indexing)
332:                         {
333:                                 throw new AnalysisException("Hit found stop search");
334:                         }
335:                 }
336:         }
337:
338:         public static int[] getNodeOffset(INode node)
339:         {
340:•                if (node instanceof PDefinition)
341:                 {
342:                         return getNodeOffset(((PDefinition) node).getLocation());
343:•                } else if (node instanceof PExp)
344:                 {
345:                         return getNodeOffset(((PExp) node).getLocation());
346:•                } else if (node instanceof PStm)
347:                 {
348:                         return getNodeOffset(((PStm) node).getLocation());
349:•                } else if (node instanceof AFieldField)
350:                 {
351:                         return getNodeOffset(((AFieldField) node).getTagname().getLocation());
352:•                } else if (node instanceof PImport)
353:                 {
354:                         return getNodeOffset(((PImport) node).getLocation());
355:•                } else if (node instanceof PExport)
356:                 {
357:                         return getNodeOffset(((PExport) node).getLocation());
358:•                } else if (node instanceof AFromModuleImports)
359:                 {
360:                         return getNodeOffset(((AFromModuleImports) node).getName().getLocation());
361:•                } else if (node instanceof ImportsContainer)
362:                 {
363:                         return getNodeOffset(((ImportsContainer) node).getImports().getImports().getFirst());
364:•                } else if (node instanceof AModuleImports)
365:                 {
366:                         return getNodeOffset(((AModuleImports) node).getName().getLocation());
367:•                } else if (node instanceof AModuleModules)
368:                 {
369:                         return getNodeOffset(((AModuleModules) node).getName().getLocation());
370:                 }
371:                 return new int[] { -1, -1 };
372:         }
373:
374:         public static int[] getNodeOffset(ILexLocation location)
375:         {
376:                 return new int[] { location.getStartOffset() - 1,
377:                                 location.getEndOffset() - location.getStartOffset() };
378:         }
379: }