/** * Copyright (c) 2008 Aptana, Inc. * * 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. If redistributing this code, * this entire header must remain intact. * * This file is based on a JDT equivalent: ******************************************************************************* * Copyright (c) 2000, 2006 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.rubypeople.rdt.internal.ui.wizards.buildpaths; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.rubypeople.rdt.core.ILoadpathAttribute; import org.rubypeople.rdt.core.ILoadpathContainer; import org.rubypeople.rdt.core.ILoadpathEntry; import org.rubypeople.rdt.core.IRubyProject; import org.rubypeople.rdt.core.LoadpathContainerInitializer; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.core.RubyModelException; import org.rubypeople.rdt.internal.corext.util.RubyModelUtil; import org.rubypeople.rdt.internal.ui.RubyPlugin; import org.rubypeople.rdt.launching.RubyRuntime; public class CPListElement { public static final String EXCLUSION = "exclusion"; //$NON-NLS-1$ public static final String INCLUSION = "inclusion"; //$NON-NLS-1$ private IRubyProject fProject; private int fEntryKind; private IPath fPath, fOrginalPath; private IResource fResource; private boolean fIsExported; private boolean fIsMissing; private Object fParentContainer; private ILoadpathEntry fCachedEntry; private ArrayList fChildren; private IPath fLinkTarget, fOrginalLinkTarget; public CPListElement(IRubyProject project, int entryKind, IPath path, IResource res) { this(null, project, entryKind, path, res); } public CPListElement(Object parent, IRubyProject project, int entryKind, IPath path, IResource res) { this(parent, project, entryKind, path, res, null); } public CPListElement(IRubyProject project, int entryKind) { this(null, project, entryKind, null, null); } public CPListElement(Object parent, IRubyProject project, int entryKind, IPath path, IResource res, IPath linkTarget) { fProject = project; fEntryKind = entryKind; fPath = path; fOrginalPath = path; fLinkTarget = linkTarget; fOrginalLinkTarget = linkTarget; fChildren = new ArrayList(); fResource = res; fIsExported = false; fIsMissing = false; fCachedEntry = null; fParentContainer = parent; switch (entryKind) { case ILoadpathEntry.CPE_SOURCE: createAttributeElement(INCLUSION, new Path[0], true); createAttributeElement(EXCLUSION, new Path[0], true); break; case ILoadpathEntry.CPE_LIBRARY: case ILoadpathEntry.CPE_VARIABLE: break; case ILoadpathEntry.CPE_PROJECT: break; case ILoadpathEntry.CPE_CONTAINER: try { ILoadpathContainer container = RubyCore.getLoadpathContainer( fPath, fProject); if (container != null) { ILoadpathEntry[] entries = container.getLoadpathEntries(); for (int i = 0; i < entries.length; i++) { ILoadpathEntry entry = entries[i]; if (entry != null) { CPListElement curr = createFromExisting(this, entry, fProject); fChildren.add(curr); } else { RubyPlugin .logErrorMessage("Null entry in container '" + fPath + "'"); //$NON-NLS-1$//$NON-NLS-2$ } } } } catch (RubyModelException e) { } break; default: } } public ILoadpathEntry getLoadpathEntry() { if (fCachedEntry == null) { fCachedEntry = newLoadpathEntry(); } return fCachedEntry; } private ILoadpathAttribute[] getLoadpathAttributes() { ArrayList res = new ArrayList(); for (int i = 0; i < fChildren.size(); i++) { Object curr = fChildren.get(i); if (curr instanceof CPListElementAttribute) { CPListElementAttribute elem = (CPListElementAttribute) curr; if (!elem.isBuiltIn() && elem.getValue() != null) { res.add(elem.newLoadpathAttribute()); } } } return (ILoadpathAttribute[]) res.toArray(new ILoadpathAttribute[res .size()]); } private ILoadpathEntry newLoadpathEntry() { ILoadpathAttribute[] extraAttributes = getLoadpathAttributes(); switch (fEntryKind) { case ILoadpathEntry.CPE_SOURCE: IPath[] inclusionPattern = (IPath[]) getAttribute(INCLUSION); IPath[] exclusionPattern = (IPath[]) getAttribute(EXCLUSION); return RubyCore.newSourceEntry(fPath, inclusionPattern, exclusionPattern, extraAttributes); case ILoadpathEntry.CPE_LIBRARY: { return RubyCore.newLibraryEntry(fPath, extraAttributes, isExported()); } case ILoadpathEntry.CPE_PROJECT: { return RubyCore.newProjectEntry(fPath, extraAttributes, isExported()); } case ILoadpathEntry.CPE_CONTAINER: { return RubyCore.newContainerEntry(fPath, extraAttributes, isExported()); } case ILoadpathEntry.CPE_VARIABLE: { return RubyCore.newVariableEntry(fPath, extraAttributes, isExported()); } default: return null; } } /** * Gets the class path entry path. * * @see ILoadpathEntry#getPath() */ public IPath getPath() { return fPath; } /** * Gets the class path entry kind. * * @see ILoadpathEntry#getEntryKind() */ public int getEntryKind() { return fEntryKind; } /** * Entries without resource are either non existing or a variable entry * External jars do not have a resource */ public IResource getResource() { return fResource; } public CPListElementAttribute setAttribute(String key, Object value) { CPListElementAttribute attribute = findAttributeElement(key); if (attribute == null) { return null; } if (key.equals(EXCLUSION) || key.equals(INCLUSION)) { Assert.isTrue(value != null || fEntryKind != ILoadpathEntry.CPE_SOURCE); } attribute.setValue(value); attributeChanged(key); return attribute; } public boolean addToExclusions(IPath path) { String key = CPListElement.EXCLUSION; return addFilter(path, key); } public boolean addToInclusion(IPath path) { String key = CPListElement.INCLUSION; return addFilter(path, key); } public boolean removeFromExclusions(IPath path) { String key = CPListElement.EXCLUSION; return removeFilter(path, key); } public boolean removeFromInclusion(IPath path) { String key = CPListElement.INCLUSION; return removeFilter(path, key); } private boolean addFilter(IPath path, String key) { IPath[] exclusionFilters = (IPath[]) getAttribute(key); if (!RubyModelUtil.isExcludedPath(path, exclusionFilters)) { IPath pathToExclude = path.removeFirstSegments( getPath().segmentCount()).addTrailingSeparator(); IPath[] newExclusionFilters = new IPath[exclusionFilters.length + 1]; System.arraycopy(exclusionFilters, 0, newExclusionFilters, 0, exclusionFilters.length); newExclusionFilters[exclusionFilters.length] = pathToExclude; setAttribute(key, newExclusionFilters); return true; } return false; } private boolean removeFilter(IPath path, String key) { IPath[] exclusionFilters = (IPath[]) getAttribute(key); IPath pathToExclude = path .removeFirstSegments(getPath().segmentCount()) .addTrailingSeparator(); if (RubyModelUtil.isExcludedPath(pathToExclude, exclusionFilters)) { List l = new ArrayList(Arrays.asList(exclusionFilters)); l.remove(pathToExclude); IPath[] newExclusionFilters = (IPath[]) l.toArray(new IPath[l .size()]); setAttribute(key, newExclusionFilters); return true; } return false; } public CPListElementAttribute findAttributeElement(String key) { for (int i = 0; i < fChildren.size(); i++) { Object curr = fChildren.get(i); if (curr instanceof CPListElementAttribute) { CPListElementAttribute elem = (CPListElementAttribute) curr; if (key.equals(elem.getKey())) { return elem; } } } return null; } public Object getAttribute(String key) { CPListElementAttribute attrib = findAttributeElement(key); if (attrib != null) { return attrib.getValue(); } return null; } private void createAttributeElement(String key, Object value, boolean builtIn) { fChildren.add(new CPListElementAttribute(this, key, value, builtIn)); } private static boolean isFiltered(Object entry, String[] filteredKeys) { if (entry instanceof CPListElementAttribute) { String key = ((CPListElementAttribute) entry).getKey(); for (int i = 0; i < filteredKeys.length; i++) { if (key.equals(filteredKeys[i])) { return true; } } } return false; } private Object[] getFilteredChildren(String[] filteredKeys) { int nChildren = fChildren.size(); ArrayList res = new ArrayList(nChildren); for (int i = 0; i < nChildren; i++) { Object curr = fChildren.get(i); if (!isFiltered(curr, filteredKeys)) { res.add(curr); } } return res.toArray(); } public Object[] getChildren(boolean hideOutputFolder) { if (hideOutputFolder && fEntryKind == ILoadpathEntry.CPE_SOURCE) { return getFilteredChildren(new String[] {}); } if (fParentContainer instanceof CPListElement) { IPath jreContainerPath = new Path(RubyRuntime.RUBY_CONTAINER); if (jreContainerPath.isPrefixOf(((CPListElement) fParentContainer) .getPath())) { // don't show access rules and native path for containers (bug // 98710) return getFilteredChildren(new String[] {}); } } if (fEntryKind == ILoadpathEntry.CPE_PROJECT) { return getFilteredChildren(new String[] {}); } return fChildren.toArray(); } public Object getParentContainer() { return fParentContainer; } private void attributeChanged(String key) { fCachedEntry = null; } private boolean canUpdateContainer() { if (fEntryKind == ILoadpathEntry.CPE_CONTAINER && fProject != null) { LoadpathContainerInitializer initializer = RubyCore .getLoadpathContainerInitializer(fPath.segment(0)); return (initializer != null && initializer .canUpdateLoadpathContainer(fPath, fProject)); } return false; } public boolean isInNonModifiableContainer() { if (fParentContainer instanceof CPListElement) { return !((CPListElement) fParentContainer).canUpdateContainer(); } return false; } /* * @see Object#equals(java.lang.Object) */ public boolean equals(Object other) { if (other != null && other.getClass().equals(getClass())) { CPListElement elem = (CPListElement) other; return getLoadpathEntry().equals(elem.getLoadpathEntry()); } return false; } /* * @see Object#hashCode() */ public int hashCode() { return fPath.hashCode() + fEntryKind; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ public String toString() { return getLoadpathEntry().toString(); } /** * Returns if a entry is missing. * * @return Returns a boolean */ public boolean isMissing() { return fIsMissing; } /** * Sets the 'missing' state of the entry. */ public void setIsMissing(boolean isMissing) { fIsMissing = isMissing; } /** * Returns if a entry is exported (only applies to libraries) * * @return Returns a boolean */ public boolean isExported() { return fIsExported; } /** * Sets the export state of the entry. */ public void setExported(boolean isExported) { if (isExported != fIsExported) { fIsExported = isExported; attributeChanged(null); } } /** * Gets the project. * * @return Returns a IRubyProject */ public IRubyProject getRubyProject() { return fProject; } public static CPListElement createFromExisting(ILoadpathEntry curr, IRubyProject project) { return createFromExisting(null, curr, project); } public static CPListElement createFromExisting(Object parent, ILoadpathEntry curr, IRubyProject project) { IPath path = curr.getPath(); IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); // get the resource IResource res = null; boolean isMissing = false; IPath linkTarget = null; switch (curr.getEntryKind()) { case ILoadpathEntry.CPE_CONTAINER: res = null; try { isMissing = project != null && (RubyCore.getLoadpathContainer(path, project) == null); } catch (RubyModelException e) { isMissing = true; } break; case ILoadpathEntry.CPE_VARIABLE: IPath resolvedPath = RubyCore.getResolvedVariablePath(path); res = null; if (resolvedPath == null) { isMissing = true; } else { isMissing = !resolvedPath.toFile().isDirectory(); } break; case ILoadpathEntry.CPE_LIBRARY: res = root.findMember(path); if (res == null) { if (root.getWorkspace().validatePath(path.toString(), IResource.FOLDER).isOK() && root.getProject(path.segment(0)).exists()) { res = root.getFolder(path); } isMissing = !path.toFile().isDirectory(); // look for external // Folders } else if (res.isLinked()) { linkTarget = res.getLocation(); } break; case ILoadpathEntry.CPE_SOURCE: path = path.removeTrailingSeparator(); res = root.findMember(path); if (res == null) { if (root.getWorkspace().validatePath(path.toString(), IResource.FOLDER).isOK()) { res = root.getFolder(path); } isMissing = true; } else if (res.isLinked()) { linkTarget = res.getLocation(); } break; case ILoadpathEntry.CPE_PROJECT: res = root.findMember(path); isMissing = (res == null); break; } CPListElement elem = new CPListElement(parent, project, curr .getEntryKind(), path, res, linkTarget); elem.setExported(curr.isExported()); elem.setAttribute(EXCLUSION, curr.getExclusionPatterns()); elem.setAttribute(INCLUSION, curr.getInclusionPatterns()); ILoadpathAttribute[] extraAttributes = curr.getExtraAttributes(); for (int i = 0; i < extraAttributes.length; i++) { ILoadpathAttribute attrib = extraAttributes[i]; elem.setAttribute(attrib.getName(), attrib.getValue()); } if (project != null && project.exists()) { elem.setIsMissing(isMissing); } return elem; } public static StringBuffer appendEncodePath(IPath path, StringBuffer buf) { if (path != null) { String str = path.toString(); buf.append('[').append(str.length()).append(']').append(str); } else { buf.append('[').append(']'); } return buf; } public static StringBuffer appendEncodedString(String str, StringBuffer buf) { if (str != null) { buf.append('[').append(str.length()).append(']').append(str); } else { buf.append('[').append(']'); } return buf; } public static StringBuffer appendEncodedFilter(IPath[] filters, StringBuffer buf) { if (filters != null) { buf.append('[').append(filters.length).append(']'); for (int i = 0; i < filters.length; i++) { appendEncodePath(filters[i], buf).append(';'); } } else { buf.append('[').append(']'); } return buf; } public StringBuffer appendEncodedSettings(StringBuffer buf) { buf.append(fEntryKind).append(';'); if (getLinkTarget() == null) { appendEncodePath(fPath, buf).append(';'); } else { appendEncodePath(fPath, buf).append('-').append('>'); appendEncodePath(getLinkTarget(), buf).append(';'); } buf.append(Boolean.valueOf(fIsExported)).append(';'); for (int i = 0; i < fChildren.size(); i++) { Object curr = fChildren.get(i); if (curr instanceof CPListElementAttribute) { CPListElementAttribute elem = (CPListElementAttribute) curr; if (elem.isBuiltIn()) { String key = elem.getKey(); if (EXCLUSION.equals(key) || INCLUSION.equals(key)) { appendEncodedFilter((IPath[]) elem.getValue(), buf) .append(';'); } } else { appendEncodedString((String) elem.getValue(), buf); } } } return buf; } public IPath getLinkTarget() { return fLinkTarget; } public void setPath(IPath path) { fCachedEntry = null; fPath = path; } public void setLinkTarget(IPath linkTarget) { fCachedEntry = null; fLinkTarget = linkTarget; } public static void insert(CPListElement element, List cpList) { int length = cpList.size(); CPListElement[] elements = (CPListElement[]) cpList .toArray(new CPListElement[length]); int i = 0; while (i < length && elements[i].getEntryKind() != element.getEntryKind()) { i++; } if (i < length) { i++; while (i < length && elements[i].getEntryKind() == element.getEntryKind()) { i++; } cpList.add(i, element); return; } switch (element.getEntryKind()) { case ILoadpathEntry.CPE_SOURCE: cpList.add(0, element); break; case ILoadpathEntry.CPE_CONTAINER: case ILoadpathEntry.CPE_LIBRARY: case ILoadpathEntry.CPE_PROJECT: case ILoadpathEntry.CPE_VARIABLE: default: cpList.add(element); break; } } public static ILoadpathEntry[] convertToLoadpathEntries( List/* <CPListElement> */cpList) { ILoadpathEntry[] result = new ILoadpathEntry[cpList.size()]; int i = 0; for (Iterator iter = cpList.iterator(); iter.hasNext();) { CPListElement cur = (CPListElement) iter.next(); result[i] = cur.getLoadpathEntry(); i++; } return result; } public static CPListElement[] createFromExisting(IRubyProject project) throws RubyModelException { ILoadpathEntry[] rawLoadpath = project.getRawLoadpath(); CPListElement[] result = new CPListElement[rawLoadpath.length]; for (int i = 0; i < rawLoadpath.length; i++) { result[i] = CPListElement.createFromExisting(rawLoadpath[i], project); } return result; } public static boolean isProjectSourceFolder(CPListElement[] existing, IRubyProject project) { IPath projPath = project.getProject().getFullPath(); for (int i = 0; i < existing.length; i++) { ILoadpathEntry curr = existing[i].getLoadpathEntry(); if (curr.getEntryKind() == ILoadpathEntry.CPE_SOURCE) { if (projPath.equals(curr.getPath())) { return true; } } } return false; } public IPath getOrginalPath() { return fOrginalPath; } public IPath getOrginalLinkTarget() { return fOrginalLinkTarget; } }