/******************************************************************************* * Copyright (c) 2017 Rogue Wave Software Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Rogue Wave Software Inc. - initial implementation *******************************************************************************/ package org.eclipse.php.internal.debug.ui.views.coverage; import java.util.*; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.internal.ui.StandardModelElementContentProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.php.core.PHPToolkitUtil; import org.eclipse.php.internal.core.PHPCorePlugin; import org.eclipse.php.internal.debug.core.pathmapper.VirtualPath; import org.eclipse.php.internal.debug.core.zend.debugger.CodeCoverageData; /** * Code coverage view content provider. */ public class CodeCoverageContentProvider implements ITreeContentProvider { private static final Object[] EMPTY = new Object[0]; private static ICodeCoverageFilter[] filters; private Map<Object, Object[]> cachedChildren; private Map<Object, CodeCoverageResult> cachedCoverage; private Map<VirtualPath, CodeCoverageData> coveredFilesMap; private Map<VirtualPath, Object> fileDataMap; private Set<VirtualPath> remoteFiles; private StandardModelElementContentProvider provider; private IProject project; public CodeCoverageContentProvider() { provider = new StandardModelElementContentProvider(true); initializeFilters(); } private static void initializeFilters() { if (filters == null) { IConfigurationElement[] elements = Platform.getExtensionRegistry() .getConfigurationElementsFor("org.eclipse.php.debug.ui.phpCodeCoverageFilter"); //$NON-NLS-1$ List<ICodeCoverageFilter> filtersList = new LinkedList<ICodeCoverageFilter>(); for (IConfigurationElement element : elements) { if (element.getName().equals("filter")) { //$NON-NLS-1$ try { filtersList.add((ICodeCoverageFilter) element.createExecutableExtension("class")); //$NON-NLS-1$ } catch (CoreException e) { PHPCorePlugin.log(e); } } } filters = (ICodeCoverageFilter[]) filtersList.toArray(new ICodeCoverageFilter[filtersList.size()]); } } /* * (non-Javadoc) * * @see * org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang. * Object) */ public Object[] getChildren(final Object element) { if (cachedChildren.containsKey(element)) { return cachedChildren.get(element); } if (element instanceof IFile || element instanceof ISourceModule) { return EMPTY; } if (element instanceof IProject && project != null && element != project) { return EMPTY; } final Object[] children = provider.getChildren(element); final List<Object> filteredChildrenList = new ArrayList<Object>(children.length); for (int i = 0; i < children.length; ++i) { final Object child = children[i]; if (child instanceof IFile || child instanceof ISourceModule) { if (!isFiltered(child)) { filteredChildrenList.add(child); } } else if (hasChildren(child)) { filteredChildrenList.add(child); } } final Object[] result = filteredChildrenList.toArray(); cachedChildren.put(element, result); return result; } /* * (non-Javadoc) * * @see * org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java * .lang.Object) */ public Object[] getElements(final Object inputElement) { if (coveredFilesMap.isEmpty()) { return new Object[] {}; } cachedChildren = new HashMap<Object, Object[]>(0); cachedCoverage = new HashMap<Object, CodeCoverageResult>(0); remoteFiles = new HashSet<VirtualPath>(); // Filter out non-relevant elements: HashMap<VirtualPath, Object> filteredDataMap = new HashMap<VirtualPath, Object>(); for (VirtualPath path : fileDataMap.keySet()) { boolean isFiltered = false; for (ICodeCoverageFilter filter : filters) { if (filter.isFiltered(path)) { isFiltered = true; break; } } if (!isFiltered) { filteredDataMap.put(path, fileDataMap.get(path)); } } remoteFiles.addAll(filteredDataMap.keySet()); Object[] elements = getChildren(inputElement); Object[] newElements = new Object[elements.length + remoteFiles.size()]; System.arraycopy(elements, 0, newElements, 0, elements.length); int c = elements.length; Iterator<VirtualPath> i = remoteFiles.iterator(); while (i.hasNext()) { VirtualPath key = i.next(); newElements[c++] = filteredDataMap.get(key); } return newElements; } /* * (non-Javadoc) * * @see * org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object * ) */ public Object getParent(final Object element) { return provider.getParent(element); } /* * (non-Javadoc) * * @see * org.eclipse.php.ui.StandardPHPElementContentProvider#hasChildrenInternal * (java.lang.Object) */ public boolean hasChildren(final Object element) { boolean hasChildren = provider.hasChildren(element); if (!hasChildren) { return false; } if (element instanceof IFile || element instanceof ISourceModule) { return false; } if (element instanceof IProject && project != null && element != project) { return false; } hasChildren = false; final Object[] children = getChildren(element); for (int i = 0; i < children.length; ++i) { hasChildren |= hasChildren(children[i]) | !isFiltered(children[i]); } return hasChildren; } /* * (non-Javadoc) * * @see * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface * .viewers.Viewer, java.lang.Object, java.lang.Object) */ public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { provider.inputChanged(viewer, oldInput, newInput); } /* * (non-Javadoc) * * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ public void dispose() { provider.dispose(); } public CodeCoverageResult getCodeCoverageResult(final Object element) { if (cachedCoverage.containsKey(element)) { return cachedCoverage.get(element); } final CodeCoverageResult coverageResult; CodeCoverageData coverageData = null; if (element instanceof IModelElement) { ISourceModule sourceModule = PHPToolkitUtil.getSourceModule((IModelElement) element); if (sourceModule != null) { coverageData = coveredFilesMap.get(new VirtualPath(sourceModule.getResource().getName())); } } else if (element instanceof IFile) { coverageData = coveredFilesMap.get(new VirtualPath(((IFile) element).getFullPath().toString())); } else { coverageData = coveredFilesMap.get(new VirtualPath(element.toString())); } if (coverageData != null) { final int linesNum = coverageData.getLinesNum(); int linesCovered = 0; int significantLines = 0; final byte[] coverageBitmask = coverageData.getCoverageBitmask(); for (int i = 1; i <= linesNum; ++i) { if (coverageBitmask.length > i / 8) { if ((coverageBitmask[i / 8] >> i % 8 & 0x1) == (byte) 1) { ++linesCovered; } } } final byte[] significanceBitmask = coverageData.getSignificanceBitmask(); if (significanceBitmask != null) { for (int i = 1; i <= linesNum; ++i) { if (significanceBitmask.length > i / 8) { if ((significanceBitmask[i / 8] >> i % 8 & 0x1) == (byte) 1) { ++significantLines; } else if ((coverageBitmask[i / 8] >> i % 8 & 0x1) == (byte) 1) { ++significantLines; // --linesCovered; } } } } else { significantLines = -1; } coverageResult = new CodeCoverageResult(linesNum, linesCovered, significantLines, 1); } else if (element instanceof IModelElement) { // TODO calculate inner elements coverage coverageResult = new CodeCoverageResult(0, 0, -1, 0); } else { coverageResult = new CodeCoverageResult(0, 0, -1, 0); final Object[] children = getChildren(element); for (int i = 0; i < children.length; ++i) { coverageResult.addCoverageResult(getCodeCoverageResult(children[i])); } cachedCoverage.put(element, coverageResult); } return coverageResult; } public CodeCoverageData getCoverageData(final String remoteFile) { return coveredFilesMap.get(new VirtualPath(remoteFile)); } public CodeCoverageData getCoverageData(final ISourceModule fileData) { return coveredFilesMap.get(new VirtualPath(fileData.getPath().toString())); } public CodeCoverageData getCoverageData(final IFile file) { return coveredFilesMap.get(new VirtualPath(file.getFullPath().toString())); } public void setCoveredFiles(final CodeCoverageData[] coveredFiles) { if (coveredFiles == null) { coveredFilesMap = new HashMap<VirtualPath, CodeCoverageData>(0); return; } coveredFilesMap = new HashMap<VirtualPath, CodeCoverageData>(coveredFiles.length); fileDataMap = new HashMap<VirtualPath, Object>(coveredFiles.length); for (CodeCoverageData element : coveredFiles) { final ISourceModule fileData = (ISourceModule) element.getAdapter(ISourceModule.class); if (fileData != null) { VirtualPath key = new VirtualPath(fileData.getPath().toString()); coveredFilesMap.put(key, element); fileDataMap.put(key, fileData); } else { String localPath = element.getLocalFileName(); IFile localFile = ResourcesPlugin.getWorkspace().getRoot().getFile(Path.fromOSString(localPath)); if (localFile.exists()) { VirtualPath key = new VirtualPath(localFile.getFullPath().toString()); coveredFilesMap.put(key, element); fileDataMap.put(key, localFile); } else { VirtualPath key = new VirtualPath(localPath); coveredFilesMap.put(key, element); fileDataMap.put(key, localPath); } } } } public void setProject(IProject project) { this.project = project; } private boolean isFiltered(final Object child) { if (child instanceof IModelElement) { final ISourceModule fileData = PHPToolkitUtil.getSourceModule((IModelElement) child); if (fileData != null) { String name = fileData.getResource().getName(); if (coveredFilesMap.containsKey(new VirtualPath(name))) { remoteFiles.remove(name); return false; } } } if (child instanceof IFile) { IFile localFile = (IFile) child; if (coveredFilesMap.containsKey(new VirtualPath(localFile.getFullPath().toString()))) { remoteFiles.remove(localFile.getFullPath().toString()); return false; } } return true; } }