/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ /* * Created on Oct 14, 2004 * * @author Fabio Zadrozny */ package org.python.pydev.debug.codecoverage; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.python.pydev.core.callbacks.CallbackWithListeners; import org.python.pydev.core.callbacks.ICallbackWithListeners; import org.python.pydev.core.tooltips.presenter.StyleRangeWithCustomData; import com.aptana.shared_core.string.FastStringBuffer; import com.aptana.shared_core.structure.Tuple; /** * * The structure is as follows: * * folders: contains a link to all the folder nodes. * files: contains a link to all the file nodes. * * the folder contains a structure that allows us to get folder nodes that are below it. * * @author Fabio Zadrozny */ public class CoverageCache { public Map<File, ICoverageNode> folders = new HashMap<File, ICoverageNode>(); public Map<File, ICoverageNode> files = new HashMap<File, ICoverageNode>(); public static final ICallbackWithListeners<StyleRange> onStyleCreated = new CallbackWithListeners<StyleRange>(); /** * * @param node */ public void addFolder(File node) { FolderNode c = new FolderNode(); c.node = node; folders.put(node, c); } /** * * @param node * @param parent */ public void addFolder(File node, File parent) { FolderNode parentNode = (FolderNode) getFolder(parent); FolderNode newNode = new FolderNode(); newNode.node = node; if (parentNode == null) { throw new RuntimeException("The folder being added:" + node.toString() + " didn't have its parent found."); } parentNode.subFolders.put(node, newNode); folders.put(node, newNode); } public FolderNode getFolder(File obj) { return (FolderNode) getIt(obj, folders); } public ICoverageNode getFile(File obj) { return getIt(obj, files); } /** * @param obj * @return */ private ICoverageNode getIt(File obj, Map<File, ICoverageNode> m) { ICoverageNode object = m.get(obj); if (object == null) { for (Iterator<File> iter = m.keySet().iterator(); iter.hasNext();) { Object element = iter.next(); if (element.equals(obj)) { return m.get(element); } } } return object; } /** * * @param node * @param parent * @param stmts * @param miss * @param notExecuted */ public void addFile(File node, File parent, int stmts, int miss, String notExecuted) { FolderNode folderNode = (FolderNode) getFolder(parent); if (folderNode == null) { throw new RuntimeException("A file node (" + node.toString() + ")MUST have a related folder node."); } FileNode fileNode = new FileNode(); fileNode.miss = miss; fileNode.node = node; fileNode.notExecuted = notExecuted; fileNode.stmts = stmts; folderNode.files.put(node, fileNode); files.put(node, fileNode); } /** * * @param node * @param parent * @param stmts * @param miss * @param notExecuted */ public void addFile(File node, File parent, String desc) { FolderNode folderNode = (FolderNode) getFolder(parent); if (folderNode == null) { throw new RuntimeException("A file node (" + node.toString() + ")MUST have a related folder node."); } ErrorFileNode fileNode = new ErrorFileNode(); fileNode.node = node; fileNode.desc = desc; folderNode.files.put(node, fileNode); files.put(node, fileNode); } public List<ICoverageNode> getFiles(File node) throws NodeNotFoudException { FolderNode folderNode = (FolderNode) getFolder(node); if (folderNode == null) { ICoverageNode fileNode = getFile(node); if (fileNode == null) { throw new NodeNotFoudException("The node has not been found: " + node.toString()); } ArrayList<ICoverageNode> list = new ArrayList<ICoverageNode>(); list.add(fileNode); return list; } //we have a folder node. ArrayList<ICoverageNode> list = new ArrayList<ICoverageNode>(); recursivelyFillList(folderNode, list); return list; } /** * @param folderNode * @param list */ private void recursivelyFillList(FolderNode folderNode, ArrayList<ICoverageNode> list) { list.addAll(sortCollectionWithCoverageLeafNodes(folderNode.files.values())); //get its sub folders for (Iterator<ICoverageNode> it = sortCollectionWithToString(folderNode.subFolders.values()).iterator(); it .hasNext();) { recursivelyFillList((FolderNode) it.next(), list); } } private List<ICoverageLeafNode> sortCollectionWithCoverageLeafNodes(Collection<ICoverageLeafNode> collection) { List<ICoverageLeafNode> vals = new ArrayList<ICoverageLeafNode>(collection); Collections.sort(vals, new Comparator<Object>() { public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); } }); return vals; } private List<ICoverageNode> sortCollectionWithToString(Collection<ICoverageNode> collection) { List<ICoverageNode> vals = new ArrayList<ICoverageNode>(collection); Collections.sort(vals, new Comparator<Object>() { public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); } }); return vals; } /** * * @param node * @return an Object such that the positions contain: * 0 - string representing the data received, such as: * * Name Stmts Miss Cover Missing * --------------------------------------------- * file_to_test 7 6 85% 8 * file_to_test2 13 9 69% 12-14, 17 * --------------------------------------------- * TOTAL 20 15 75% * */ public Tuple<String, List<StyleRange>> getStatistics(String baseLocation, File node) { List<StyleRange> ranges = new ArrayList<StyleRange>(); if (baseLocation == null) { baseLocation = ""; } FastStringBuffer buffer = new FastStringBuffer(); try { List<ICoverageNode> list = getFiles(node); //array of FileNode //40 chars for name. int nameNumberOfColumns = PyCoveragePreferences.getNameNumberOfColumns(); buffer.append("Name").appendN(' ', nameNumberOfColumns - 4) .append(" Stmts Miss Cover Missing\n"); buffer.appendN('-', nameNumberOfColumns); buffer.append("-------------------------------------\n"); int totalMiss = 0; int totalStmts = 0; for (ICoverageNode element : list) { if (element instanceof FileNode) { //it may have been an error node... FileNode fileNode = (FileNode) element; int start = buffer.length(); fileNode.appendToBuffer(buffer, baseLocation, nameNumberOfColumns).append("\n"); int len = buffer.indexOf(' ', start) - start; StyleRangeWithCustomData styleRange = new StyleRangeWithCustomData(start, len, null, null); styleRange.underline = true; try { styleRange.underlineStyle = SWT.UNDERLINE_LINK; } catch (Throwable e) { //Ignore (not available on earlier versions of eclipse) } onStyleCreated.call(styleRange); ranges.add(styleRange); styleRange.customData = element; totalMiss += fileNode.miss; totalStmts += fileNode.stmts; } else { buffer.append(element.toString()).append("\n"); } } buffer.appendN('-', nameNumberOfColumns); buffer.append("-------------------------------------\n"); FileNode.appendToBuffer(buffer, "TOTAL", totalStmts, totalMiss, "", nameNumberOfColumns).append("\n"); } catch (NodeNotFoudException e) { buffer.append("File has no statistics."); } return new Tuple<String, List<StyleRange>>(buffer.toString(), ranges); } /** * */ public void clear() { folders.clear(); files.clear(); } }