/******************************************************************************* * Copyright (c) 2000, 2016 QNX Software Systems 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: * QNX Software Systems - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.debug.internal.core.sourcelookup; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.eclipse.cdt.core.resources.FileStorage; import org.eclipse.cdt.debug.core.CDebugCorePlugin; import org.eclipse.cdt.debug.core.CDebugUtils; import org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocation; import org.eclipse.cdt.debug.core.sourcelookup.IDirectorySourceLocation; import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IStorage; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.osgi.util.NLS; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * * Locates source elements in a directory in the local * file system. Returns instances of <code>FileStorage</code>. * * @since Sep 23, 2002 */ public class CDirectorySourceLocation implements IDirectorySourceLocation { private static final String ELEMENT_NAME = "cDirectorySourceLocation"; //$NON-NLS-1$ private static final String ATTR_DIRECTORY = "directory"; //$NON-NLS-1$ private static final String ATTR_ASSOCIATION = "association"; //$NON-NLS-1$ private static final String ATTR_SEARCH_SUBFOLDERS = "searchSubfolders"; //$NON-NLS-1$ /** * The root directory of this source location */ private IPath fDirectory; /** * The associated path of this source location. */ private IPath fAssociation; private boolean fSearchForDuplicateFiles; private boolean fSearchSubfolders; private File[] fFolders; /** * Constructor for CDirectorySourceLocation. */ public CDirectorySourceLocation() { } /** * Constructor for CDirectorySourceLocation. */ public CDirectorySourceLocation(IPath directory, IPath association, boolean searchSubfolders) { setDirectory(directory); setAssociation(association); setSearchSubfolders(searchSubfolders); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocation#findSourceElement(String) */ @Override public Object findSourceElement(String name) throws CoreException { Object result = null; if (!isEmpty(name) && getDirectory() != null) { File file = new File(name); if (file.isAbsolute()) { result = findFileByAbsolutePath(name); } else { result = findFileByRelativePath(name); } if (result == null && getAssociation() != null) { IPath path = new Path(name); if (path.segmentCount() > 1 && getAssociation().isPrefixOf(path)) { path = getDirectory().append(path.removeFirstSegments(getAssociation().segmentCount())); result = findFileByAbsolutePath(path.toOSString()); } } } return result; } @SuppressWarnings("unchecked") @Override public <T> T getAdapter(Class<T> adapter) { if (adapter.equals(ICSourceLocation.class)) return (T) this; if (adapter.equals(CDirectorySourceLocation.class)) return (T) this; if (adapter.equals(IPath.class)) return (T) getDirectory(); return null; } /** * Sets the directory in which source elements will be searched for. * * @param directory a directory */ private void setDirectory(IPath directory) { fDirectory = directory; } /** * Returns the root directory of this source location. * * @return directory */ @Override public IPath getDirectory() { return fDirectory; } public void getDirectory(IPath path) { fDirectory = path; } public void setAssociation(IPath association) { fAssociation = association; } @Override public IPath getAssociation() { return fAssociation; } @SuppressWarnings("unchecked") private Object findFileByAbsolutePath(String name) { File file = new File(name); if (!file.isAbsolute()) return null; File[] folders = getFolders(); if (folders != null) { LinkedList<Object> list = new LinkedList<Object>(); for (int i = 0; i < folders.length; ++i) { Object result = findFileByAbsolutePath(folders[i], name); if (result instanceof List) { if (searchForDuplicateFiles()) { list.addAll((List<Object>) result); } else { return list.getFirst(); } } else if (result != null) { if (searchForDuplicateFiles()) { list.add(result); } else { return result; } } } if (list.size() > 0) return (list.size() == 1) ? list.getFirst() : list; } return null; } private Object findFileByAbsolutePath(File folder, String name) { File file = new File(name); if (!file.isAbsolute()) return null; IPath filePath = new Path(name); IPath path = new Path(folder.getAbsolutePath()); IPath association = getAssociation(); if (!isPrefix(path, filePath) || path.segmentCount() + 1 != filePath.segmentCount()) { if (association != null && isPrefix(association, filePath) && association.segmentCount() + 1 == filePath.segmentCount()) { filePath = path.append(filePath.removeFirstSegments(association.segmentCount())); } else { return null; } } // Try for a file in another workspace project IFile[] wsFiles = ResourceLookup.findFilesForLocation(filePath); LinkedList<IFile> list = new LinkedList<IFile>(); for (int j = 0; j < wsFiles.length; ++j) if (wsFiles[j].exists()) { if (!searchForDuplicateFiles()) return wsFiles[j]; list.add(wsFiles[j]); } if (list.size() > 0) return (list.size() == 1) ? list.getFirst() : list; file = filePath.toFile(); if (file.exists() && file.isFile()) { return createExternalFileStorage(filePath); } return null; } @SuppressWarnings("unchecked") private Object findFileByRelativePath(String fileName) { File[] folders = getFolders(); if (folders != null) { LinkedList<Object> list = new LinkedList<Object>(); for (int i = 0; i < folders.length; ++i) { Object result = findFileByRelativePath(folders[i], fileName); if (result instanceof List) { if (searchForDuplicateFiles()) { list.addAll((List<Object>) result); } else { return list.getFirst(); } } else if (result != null) { if (searchForDuplicateFiles()) { list.add(result); } else { return result; } } } if (list.size() > 0) return (list.size() == 1) ? list.getFirst() : list; } return null; } private Object findFileByRelativePath(File folder, String fileName) { IPath path = new Path(folder.getAbsolutePath()); path = path.append(fileName); File file = path.toFile(); if (file.exists() && file.isFile()) { path = new Path(file.getAbsolutePath()); IFile[] wsFiles = ResourceLookup.findFilesForLocation(path); LinkedList<IFile> list = new LinkedList<IFile>(); for (int j = 0; j < wsFiles.length; ++j) if (wsFiles[j].exists()) { if (!searchForDuplicateFiles()) return wsFiles[j]; list.add(wsFiles[j]); } if (list.size() > 0) return (list.size() == 1) ? list.getFirst() : list; return createExternalFileStorage(path); } return null; } private IStorage createExternalFileStorage(IPath path) { return new FileStorage(path); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocation#getMemento() */ @Override public String getMemento() throws CoreException { Document document = null; Throwable ex = null; try { document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); Element node = document.createElement(ELEMENT_NAME); document.appendChild(node); node.setAttribute(ATTR_DIRECTORY, getDirectory().toOSString()); if (getAssociation() != null) node.setAttribute(ATTR_ASSOCIATION, getAssociation().toOSString()); node.setAttribute(ATTR_SEARCH_SUBFOLDERS, String.valueOf(searchSubfolders())); return CDebugUtils.serializeDocument(document); } catch (ParserConfigurationException e) { ex = e; } catch (IOException e) { ex = e; } catch (TransformerException e) { ex = e; } abort(NLS.bind(InternalSourceLookupMessages.CDirectorySourceLocation_0, getDirectory().toOSString()), ex); // execution will not reach here return null; } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocation#initializeFrom(java.lang.String) */ @Override public void initializeFrom(String memento) throws CoreException { Exception ex = null; try { Element root = null; DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); StringReader reader = new StringReader(memento); InputSource source = new InputSource(reader); root = parser.parse(source).getDocumentElement(); String dir = root.getAttribute(ATTR_DIRECTORY); if (isEmpty(dir)) { abort(InternalSourceLookupMessages.CDirectorySourceLocation_1, null); } else { IPath path = new Path(dir); if (path.isValidPath(dir) && path.toFile().isDirectory() && path.toFile().exists()) { setDirectory(path); } else { abort(NLS.bind(InternalSourceLookupMessages.CDirectorySourceLocation_2, dir), null); } } dir = root.getAttribute(ATTR_ASSOCIATION); if (isEmpty(dir)) { setAssociation(null); } else { IPath path = new Path(dir); if (path.isValidPath(dir)) { setAssociation(path); } else { setAssociation(null); } } setSearchSubfolders(Boolean.valueOf(root.getAttribute(ATTR_SEARCH_SUBFOLDERS)).booleanValue()); return; } catch (ParserConfigurationException e) { ex = e; } catch (SAXException e) { ex = e; } catch (IOException e) { ex = e; } abort(InternalSourceLookupMessages.CDirectorySourceLocation_3, ex); } /** * Throws an internal error exception */ private void abort(String message, Throwable e) throws CoreException { IStatus s = new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), CDebugCorePlugin.INTERNAL_ERROR, message, e); throw new CoreException(s); } private boolean isEmpty(String string) { return string == null || string.length() == 0; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj instanceof IDirectorySourceLocation) { IPath dir = ((IDirectorySourceLocation)obj).getDirectory(); IPath association = ((IDirectorySourceLocation)obj).getAssociation(); if (dir == null) return false; boolean result = dir.equals(getDirectory()); if (result) { if (association == null && getAssociation() == null) return true; if (association != null) return association.equals(getAssociation()); } } return false; } private boolean isPrefix(IPath prefix, IPath path) { int segCount = prefix.segmentCount(); if (segCount >= path.segmentCount()) return false; String prefixString = prefix.toOSString(); String pathString = path.removeLastSegments(path.segmentCount() - segCount).toOSString(); return prefixString.equalsIgnoreCase(pathString); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocation#setSearchForDuplicateFiles(boolean) */ @Override public void setSearchForDuplicateFiles(boolean search) { fSearchForDuplicateFiles = search; } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocation#searchForDuplicateFiles() */ @Override public boolean searchForDuplicateFiles() { return fSearchForDuplicateFiles; } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.sourcelookup.IDirectorySourceLocation#searchSubfolders() */ @Override public boolean searchSubfolders() { return fSearchSubfolders; } public void setSearchSubfolders(boolean search) { resetFolders(); fSearchSubfolders = search; } protected File[] getFolders() { if (fFolders == null) initializeFolders(); return fFolders; } protected void resetFolders() { fFolders = null; } private void initializeFolders() { if (getDirectory() != null) { ArrayList<File> list = new ArrayList<File>(); File root = getDirectory().toFile(); list.add(root); if (searchSubfolders()) list.addAll(getFileFolders(root)); fFolders = list.toArray(new File[list.size()]); } } private List<File> getFileFolders(File file) { ArrayList<File> list = new ArrayList<File>(); File[] folders = file.listFiles( new FileFilter() { @Override public boolean accept(File pathname) { return pathname.isDirectory(); } }); list.addAll(Arrays.asList(folders)); for (int i = 0; i < folders.length; ++i) list.addAll(getFileFolders(folders[i])); return list; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return (getDirectory() != null) ? getDirectory().toOSString() : ""; //$NON-NLS-1$ } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocation#dispose() */ @Override public void dispose() { } }