/******************************************************************************* * * Copyright (c) 2008 Fujitsu Services Ltd. * * Author: Nick Battle * * This file is part of VDMJ. * * VDMJ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VDMJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VDMJ. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ package org.overture.ast.lex; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Vector; import org.overture.ast.intf.lex.ILexLocation; import org.overture.ast.intf.lex.ILexNameToken; import org.overture.ast.node.ExternalNode; /** * A class to hold the location of a token. */ public class LexLocation implements Serializable, ExternalNode, ILexLocation { @Override public LexLocation clone() { LexLocation location = new LexLocation(file, module, startLine, startPos, endLine, endPos, startOffset, endOffset); location.hits = hits; location.executable = executable; return location; } public static boolean absoluteToStringLocation = true; private static final long serialVersionUID = 1L; /** A collection of all LexLocation objects. */ private static List<LexLocation> allLocations = new Vector<LexLocation>(); /** A unique map of LexLocation objects, for rapid searching. */ private static Map<LexLocation, LexLocation> uniqueLocations = new HashMap<LexLocation, LexLocation>(); /** A map of f/op/class names to their lexical span, for coverage. */ private static Map<LexNameToken, LexLocation> nameSpans = new HashMap<LexNameToken, LexLocation>();// TODO /** True if the location is executable. */ private boolean executable = false; /** The filename of the token. */ public final File file; /** The module/class name of the token. */ public final String module; /** The line number of the start of the token. */ public final int startLine; /** The character position of the start of the token. */ public final int startPos; /** The last line of the token. */ public final int endLine; /** The character position of the end of the token. */ public final int endPos; public final int startOffset; public final int endOffset; /** The number of times the location has been executed. */ public long hits = 0; /** * Create a location with the given fields. * * @param file * @param module * @param startLine * @param startPos * @param endLine * @param endPos * @param startOffset * @param endOffset */ public LexLocation(File file, String module, int startLine, int startPos, int endLine, int endPos, int startOffset, int endOffset) { this.file = file; this.module = module; this.startLine = startLine; this.startPos = startPos; this.endLine = endLine; this.endPos = endPos; this.startOffset = startOffset; this.endOffset = endOffset; synchronized (allLocations) { allLocations.add(this); uniqueLocations.put(this, this); } } public LexLocation(String filePath, String module, int startLine, int startPos, int endLine, int endPos, int startOffset, int endOffset) { this.file = new File(filePath); this.module = module; this.startLine = startLine; this.startPos = startPos; this.endLine = endLine; this.endPos = endPos; this.startOffset = startOffset; this.endOffset = endOffset; synchronized (allLocations) { allLocations.add(this); uniqueLocations.put(this, this); } } /** * Create a default location. */ public LexLocation() { this(new File("?"), "?", 0, 0, 0, 0, 0, 0); } @Override public boolean getExecutable() { return executable; } @Override public long getHits() { return hits; } @Override public void setHits(long hits) { this.hits = hits; } @Override public File getFile() { return file; } @Override public String getModule() { return module; } @Override public int getStartLine() { return startLine; } @Override public int getStartPos() { return startPos; } @Override public int getEndLine() { return endLine; } @Override public int getEndPos() { return endPos; } @Override public int getStartOffset() { return startOffset; } @Override public int getEndOffset() { return endOffset; } @Override public String toString() { if (file.getPath().equals("?")) { return ""; // Default LexLocation has no location string } else if (module == null || module.equals("")) { return "in '" + (absoluteToStringLocation ? file : file.getName()) + "' at line " + startLine + ":" + startPos; } else { return "in '" + module + "' (" + (absoluteToStringLocation ? file : file.getName()) + ") at line " + startLine + ":" + startPos; } } public String toShortString() { if (file.getPath().equals("?")) { return ""; // Default LexLocation has no location string } else { return "at " + startLine + ":" + startPos; } } /** * Method to resolve existing locations during de-serialise - as used during deep copy. This is to avoid problems * with coverage. */ private Object readResolve() throws ObjectStreamException { LexLocation existing = uniqueLocations.get(this); if (existing == null) { return this; } else { return existing; } } @Override public boolean equals(Object other) { if (other instanceof ILexLocation) { ILexLocation lother = (ILexLocation) other; return startPos == lother.getStartPos() && startLine == lother.getStartLine() && module.equals(lother.getModule()) && file.equals(lother.getFile()); } return false; } @Override public int hashCode() { return file.hashCode() + module.hashCode() + startLine + startPos; } public boolean within(ILexLocation span) { return (startLine > span.getStartLine() || startLine == span.getStartLine() && startPos >= span.getStartPos()) && (startLine <= span.getEndLine() || startLine == span.getEndLine() && startPos < span.getEndPos()) && file.equals(span.getFile()); } public void executable(boolean exe) { executable = exe; } public void hit() { if (executable) { hits++; } } public static void clearLocations() { synchronized (allLocations) { for (LexLocation loc : allLocations) { loc.hits = 0; } } } public static void resetLocations() { synchronized (allLocations) { allLocations = new Vector<LexLocation>(); } synchronized (uniqueLocations) { uniqueLocations = new HashMap<LexLocation, LexLocation>(); } // synchronized (locationToAstNode) // { // locationToAstNode = new Hashtable<LexLocation, INode>(); // } // // synchronized (nameSpans) // { // nameSpans = new HashMap<LexNameToken, LexLocation>(); // } } public static void clearAfter(File file, int linecount, int charpos) { // Called from the LexTokenReader's pop method, to remove any // locations "popped". We assume any pushes are on the end of // the vector. synchronized (allLocations) { ListIterator<LexLocation> it = allLocations.listIterator(allLocations.size()); while (it.hasPrevious()) { LexLocation l = it.previous(); if (!l.file.equals(file) || l.startLine < linecount || l.startLine == linecount && l.startPos < charpos) { break; } else { it.remove(); uniqueLocations.remove(l); } } } } public static void addSpan(LexNameToken name, LexToken upto) { LexLocation span = new LexLocation(name.location.getFile(), name.location.getModule(), name.location.getStartLine(), name.location.getStartPos(), upto.location.getEndLine(), upto.location.getEndPos(), upto.location.getStartOffset(), upto.location.getEndOffset()); nameSpans.put(name, span); } public static LexNameList getSpanNames(File filename) { LexNameList list = new LexNameList(); for (LexNameToken name : nameSpans.keySet()) { LexLocation span = nameSpans.get(name); if (span.file.equals(filename)) { list.add(name); } } return list; } public static float getSpanPercent(ILexNameToken name) { int hits = 0; int misses = 0; LexLocation span = null; synchronized (nameSpans) { span = nameSpans.get(name); } synchronized (allLocations) { for (LexLocation l : removeDuplicates(allLocations)) { if (l.executable && l.within(span)) { if (l.hits > 0) { hits++; } else { misses++; } } } } int sum = hits + misses; return sum == 0 ? 0 : (float) (1000 * hits / sum) / 10; // NN.N% } public static long getSpanCalls(ILexNameToken name) { // The assumption is that the first executable location in // the span for the name is hit as many time as the span is called. LexLocation span = null; synchronized (nameSpans) { span = nameSpans.get(name); } synchronized (allLocations) { for (LexLocation l : removeDuplicates(allLocations)) { if (l.executable && l.within(span)) { return l.hits; } } } return 0; } public static List<Integer> getHitList(File file) { //FIXME skip lex location in other files // idea: if !lextLocation.getFile().equals(file) then continue; List<Integer> hits = new Vector<Integer>(); synchronized (allLocations) { for (LexLocation l : removeDuplicates(allLocations)) { if (l.hits > 0 && l.file.equals(file)) { hits.add(l.startLine); } } } return hits; } public static List<Integer> getMissList(File file) { List<Integer> misses = new Vector<Integer>(); synchronized (allLocations) { for (LexLocation l : removeDuplicates(allLocations)) { if (l.hits == 0 && l.file.equals(file)) { misses.add(l.startLine); } } } return misses; } public static List<Integer> getSourceList(File file) { List<Integer> lines = new Vector<Integer>(); int last = 0; synchronized (allLocations) { for (LexLocation l : allLocations) { if (l.executable && l.startLine != last && l.file.equals(file)) { lines.add(l.startLine); last = l.startLine; } } } return lines; } public static Map<Integer, List<LexLocation>> getHitLocations(File file) { Map<Integer, List<LexLocation>> map = new HashMap<Integer, List<LexLocation>>(); synchronized (allLocations) { for (LexLocation l : removeDuplicates(allLocations)) { if (l.executable && l.hits > 0 && l.file.equals(file)) { List<LexLocation> list = map.get(l.startLine); if (list == null) { list = new Vector<LexLocation>(); map.put(l.startLine, list); } list.add(l); } } } return map; } public static float getHitPercent(File file) { int hits = 0; int misses = 0; synchronized (allLocations) { for (LexLocation l : removeDuplicates(allLocations)) { if (l.file.equals(file) && l.executable) { if (l.hits > 0) { hits++; } else { misses++; } } } } int sum = hits + misses; return sum == 0 ? 0 : (float) (1000 * hits / sum) / 10; // NN.N% } public static Map<Integer, List<LexLocation>> getMissLocations(File file) { Map<Integer, List<LexLocation>> map = new HashMap<Integer, List<LexLocation>>(); synchronized (allLocations) { for (LexLocation l : removeDuplicates(allLocations)) { if (l.executable && l.hits == 0 && l.file.equals(file)) { List<LexLocation> list = map.get(l.startLine); if (list == null) { list = new Vector<LexLocation>(); map.put(l.startLine, list); } list.add(l); } } } return map; } public static List<LexLocation> getSourceLocations(File file) { List<LexLocation> locations = new Vector<LexLocation>(); synchronized (allLocations) { for (LexLocation l : allLocations) { if (l.executable && l.file.equals(file)) { locations.add(l); } } } return locations; } public static void mergeHits(File source, File coverage) throws IOException { List<LexLocation> locations = getSourceLocations(source); BufferedReader br = new BufferedReader(new FileReader(coverage)); String line = br.readLine(); while (line != null) { if (line.charAt(0) == '+') { // Hit lines are "+line from-to=hits" int s1 = line.indexOf(' '); int s2 = line.indexOf('-'); int s3 = line.indexOf('='); int lnum = Integer.parseInt(line.substring(1, s1)); int from = Integer.parseInt(line.substring(s1 + 1, s2)); int to = Integer.parseInt(line.substring(s2 + 1, s3)); int hits = Integer.parseInt(line.substring(s3 + 1)); for (LexLocation l : locations) // Only executable locations { if (l.startLine == lnum && l.startPos == from && l.endPos == to) { l.hits += hits; break; } } } line = br.readLine(); } br.close(); } /** * This method handles the case where a location exist both with hits>0 and hits==0. It will remove the location * with hits==0 when another location hits>0 exists * * @param locations * @return the list of locations where the unwanted locations have been removed */ public static List<LexLocation> removeDuplicates(List<LexLocation> locations) { List<LexLocation> tmp = new Vector<LexLocation>(); c: for (LexLocation l1 : locations) { if (l1.hits == 0) { for (LexLocation l2 : locations) { if (l1.equals(l2) && l2.hits > 0) { continue c; } } tmp.add(l1); } else { tmp.add(l1); } } return tmp; } public static List<LexLocation> getAllLocations() { return allLocations; } }