/*******************************************************************************
* Copyright © 2000, 2013 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.eclipse.edt.ide.core.internal.model;
import java.util.LinkedHashMap;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.edt.compiler.internal.core.utils.CharOperation;
import org.eclipse.edt.ide.core.model.EGLCore;
import org.eclipse.edt.ide.core.model.IEGLPathEntry;
import org.eclipse.edt.ide.core.model.IPackageFragmentRoot;
import org.w3c.dom.Element;
/**
* @see IEGLPathEntry
*/
public class EGLPathEntry implements IEGLPathEntry {
/**
* Describes the kind of eglpath entry - one of
* CPE_PROJECT, CPE_LIBRARY, CPE_SOURCE, CPE_EGLAR ,CPE_VARIABLE or CPE_CONTAINER
*/
public int entryKind;
/**
* Describes the kind of package fragment roots found on
* this eglpath entry - either K_BINARY or K_SOURCE or
* K_OUTPUT.
*/
public int contentKind;
/**
* The meaning of the path of a eglpath entry depends on its entry kind:<ul>
* <li>Source code in the current project (<code>CPE_SOURCE</code>) -
* The path associated with this entry is the absolute path to the root folder. </li>
* <li>A binary library in the current project (<code>CPE_LIBRARY</code>) - the path
* associated with this entry is the absolute path to the JAR (or root folder), and
* in case it refers to an external JAR, then there is no associated resource in
* the workbench.
* <li>A required project (<code>CPE_PROJECT</code>) - the path of the entry denotes the
* path to the corresponding project resource.</li>
* <li>A variable entry (<code>CPE_VARIABLE</code>) - the first segment of the path
* is the name of a eglpath variable. If this eglpath variable
* is bound to the path <it>P</it>, the path of the corresponding eglpath entry
* is computed by appending to <it>P</it> the segments of the returned
* path without the variable.</li>
* <li> A container entry (<code>CPE_CONTAINER</code>) - the first segment of the path is denoting
* the unique container identifier (for which a <code>EGLPathContainerInitializer</code> could be
* registered), and the remaining segments are used as additional hints for resolving the container entry to
* an actual <code>IEGLPathContainer</code>.</li>
*/
public IPath path;
/**
* Patterns allowing to exclude portions of the resource tree denoted by this entry path.
*/
public IPath[] exclusionPatterns;
private char[][] fullCharExclusionPatterns;
private final static char[][] UNINIT_PATTERNS = new char[][] { "Non-initialized yet".toCharArray() }; //$NON-NLS-1$
private String rootID;
/**
* Default exclusion pattern set
*/
public final static IPath[] EXCLUDE_NONE = {};
/**
* Describes the path to the source archive associated with this
* eglpath entry, or <code>null</code> if this eglpath entry has no
* source attachment.
* <p>
* Only library and variable eglpath entries may have source attachments.
* For library eglpath entries, the result path (if present) locates a source
* archive. For variable eglpath entries, the result path (if present) has
* an analogous form and meaning as the variable path, namely the first segment
* is the name of a eglpath variable.
*/
public IPath sourceAttachmentPath;
/**
* Describes the path within the source archive where package fragments
* are located. An empty path indicates that packages are located at
* the root of the source archive. Returns a non-<code>null</code> value
* if and only if <code>getSourceAttachmentPath</code> returns
* a non-<code>null</code> value.
*/
public IPath sourceAttachmentRootPath;
/**
* Specific output location (for this source entry)
*/
public IPath specificOutputLocation;
/**
* A constant indicating an output location.
*/
public static final int K_OUTPUT = 10;
public static final String DOT_DOT = "..";
/**
* The export flag
*/
public boolean isExported;
private boolean isBinaryProject;
/**
* Creates a class path entry of the specified kind with the given path.
*/
public EGLPathEntry(
int contentKind,
int entryKind,
IPath path,
//IPath[] inclusionPatterns, // EGLMIGRATION: Inclusion Pattern support?
IPath[] exclusionPatterns) {
this.contentKind = contentKind;
this.entryKind = entryKind;
this.path = path;
this.exclusionPatterns = exclusionPatterns;
if (exclusionPatterns.length > 0) {
this.fullCharExclusionPatterns = UNINIT_PATTERNS;
} else {
this.fullCharExclusionPatterns = null; // empty exclusion pattern means nothing is excluded
}
}
/**
* Creates a class path entry of the specified kind with the given path.
*/
public EGLPathEntry(
int contentKind,
int entryKind,
IPath path,
//IPath[] inclusionPatterns, // EGLMIGRATION: Inclusion Pattern support?
IPath[] exclusionPatterns,
IPath sourceAttachmentPath,
IPath sourceAttachmentRootPath,
IPath specificOutputLocation,
boolean isExported) {
this.contentKind = contentKind;
this.entryKind = entryKind;
this.path = path;
this.exclusionPatterns = exclusionPatterns;
if (exclusionPatterns.length > 0) {
this.fullCharExclusionPatterns = UNINIT_PATTERNS;
} else {
this.fullCharExclusionPatterns = null; // empty exclusion pattern means nothing is excluded
}
this.sourceAttachmentPath = sourceAttachmentPath;
this.sourceAttachmentRootPath = sourceAttachmentRootPath;
this.specificOutputLocation = specificOutputLocation;
this.isExported = isExported;
}
/*
* Returns a char based representation of the exclusions patterns full path.
*/
public char[][] fullExclusionPatternChars() {
if (this.fullCharExclusionPatterns == UNINIT_PATTERNS) {
int length = this.exclusionPatterns.length;
this.fullCharExclusionPatterns = new char[length][];
IPath prefixPath = path.removeTrailingSeparator();
for (int i = 0; i < length; i++) {
this.fullCharExclusionPatterns[i] =
prefixPath.append(this.exclusionPatterns[i]).toString().toCharArray();
}
}
return this.fullCharExclusionPatterns;
}
/**
* Returns the XML encoding of the class path.
*/
public void elementEncode(XMLWriter writer, IPath projectPath, boolean indent, boolean newLine) {
LinkedHashMap parameters = new LinkedHashMap();
String kindString = "";
if(!this.isBinaryProject()) {
kindString = EGLPathEntry.kindToString(this.entryKind);
if(this.contentKind == K_OUTPUT){
kindString = "output";
}
} else {
kindString = EGLPathEntry.kindToString(IEGLPathEntry.CPE_PROJECT);
}
parameters.put("kind", kindString);//$NON-NLS-1$
IPath xmlPath = this.path;
if (this.entryKind != IEGLPathEntry.CPE_VARIABLE && this.entryKind != IEGLPathEntry.CPE_CONTAINER) {
// translate to project relative from absolute (unless a device path)
if (xmlPath.isAbsolute()) {
if (projectPath != null && projectPath.isPrefixOf(xmlPath)) {
if (xmlPath.segment(0).equals(projectPath.segment(0))) {
xmlPath = xmlPath.removeFirstSegments(1);
xmlPath = xmlPath.makeRelative();
} else {
xmlPath = xmlPath.makeAbsolute();
}
}
}
}
if(isBinaryProject() && Util.isEGLARFileName(path.toOSString())) {
int binaryProjectNameIndex = path.segmentCount() -2;
xmlPath = new Path(IPath.SEPARATOR + path.segments()[binaryProjectNameIndex]);
}
parameters.put("path", String.valueOf(xmlPath));//$NON-NLS-1$
if (this.sourceAttachmentPath != null) {
xmlPath = this.sourceAttachmentPath;
// translate to project relative from absolute
if (this.entryKind != IEGLPathEntry.CPE_VARIABLE && projectPath != null && projectPath.isPrefixOf(xmlPath)) {
if (xmlPath.segment(0).equals(projectPath.segment(0))) {
xmlPath = xmlPath.removeFirstSegments(1);
xmlPath = xmlPath.makeRelative();
}
}
parameters.put("sourcepath", String.valueOf(xmlPath));//$NON-NLS-1$
}
if (this.sourceAttachmentRootPath != null) {
parameters.put("rootpath", String.valueOf(this.sourceAttachmentRootPath));//$NON-NLS-1$
}
if (this.isExported) {
parameters.put("exported", "true");//$NON-NLS-1$//$NON-NLS-2$
}
// EGLTODO: Add support for inclusion patterns?
// if (this.inclusionPatterns != null && this.inclusionPatterns.length > 0) {
// StringBuffer includeRule = new StringBuffer(10);
// for (int i = 0, max = this.inclusionPatterns.length; i < max; i++){
// if (i > 0) includeRule.append('|');
// includeRule.append(this.inclusionPatterns[i]);
// }
// parameters.put("including", String.valueOf(includeRule));//$NON-NLS-1$
// }
if (this.exclusionPatterns != null && this.exclusionPatterns.length > 0) {
StringBuffer excludeRule = new StringBuffer(10);
for (int i = 0, max = this.exclusionPatterns.length; i < max; i++){
if (i > 0) excludeRule.append('|');
excludeRule.append(this.exclusionPatterns[i]);
}
parameters.put("excluding", String.valueOf(excludeRule));//$NON-NLS-1$
}
if (this.specificOutputLocation != null) {
IPath outputLocation = this.specificOutputLocation.removeFirstSegments(1);
outputLocation = outputLocation.makeRelative();
parameters.put("output", String.valueOf(outputLocation));//$NON-NLS-1$
}
writer.printTag("eglpathentry", parameters, indent, newLine, true);//$NON-NLS-1$
}
public static IEGLPathEntry elementDecode(Element element, IPath projectPath, String projectName) {
String kindAttr = element.getAttribute("kind"); //$NON-NLS-1$
String pathAttr = element.getAttribute("path"); //$NON-NLS-1$
// ensure path is absolute
IPath path = new Path(pathAttr);
int kind = kindFromString(kindAttr);
if (kind != IEGLPathEntry.CPE_VARIABLE && kind != IEGLPathEntry.CPE_CONTAINER && !path.isAbsolute()) {
path = projectPath.append(path);
}
// source attachment info (optional)
IPath sourceAttachmentPath =
element.hasAttribute("sourcepath") //$NON-NLS-1$
? new Path(element.getAttribute("sourcepath")) //$NON-NLS-1$
: null;
if (kind != IEGLPathEntry.CPE_VARIABLE && sourceAttachmentPath != null && !sourceAttachmentPath.isAbsolute()) {
sourceAttachmentPath = projectPath.append(sourceAttachmentPath);
}
IPath sourceAttachmentRootPath =
element.hasAttribute("rootpath") //$NON-NLS-1$
? new Path(element.getAttribute("rootpath")) //$NON-NLS-1$
: null;
// exported flag (optional)
boolean isExported = element.getAttribute("exported").equals("true"); //$NON-NLS-1$ //$NON-NLS-2$
// EGLMIGRATION: Inclusion Pattern support?
// inclusion patterns (optional)
// String inclusion = element.getAttribute("including"); //$NON-NLS-1$
// IPath[] inclusionPatterns = INCLUDE_ALL;
// if (!inclusion.equals("")) { //$NON-NLS-1$
// char[][] patterns = CharOperation.splitOn('|', inclusion.toCharArray());
// int patternCount;
// if ((patternCount = patterns.length) > 0) {
// inclusionPatterns = new IPath[patternCount];
// for (int j = 0; j < patterns.length; j++){
// inclusionPatterns[j] = new Path(new String(patterns[j]));
// }
// }
// }
// exclusion patterns (optional)
String exclusion = element.getAttribute("excluding"); //$NON-NLS-1$
IPath[] exclusionPatterns = EXCLUDE_NONE;
if (!exclusion.equals("")) { //$NON-NLS-1$
char[][] patterns = CharOperation.splitOn('|', exclusion.toCharArray());
int patternCount;
if ((patternCount = patterns.length) > 0) {
exclusionPatterns = new IPath[patternCount];
for (int j = 0; j < patterns.length; j++){
exclusionPatterns[j] = new Path(new String(patterns[j]));
}
}
}
// custom output location
IPath outputLocation = element.hasAttribute("output") ? projectPath.append(element.getAttribute("output")) : null; //$NON-NLS-1$ //$NON-NLS-2$
// recreate the CP entry
switch (kind) {
case IEGLPathEntry.CPE_PROJECT :
return EGLCore.newProjectEntry(path, isExported);
case IEGLPathEntry.CPE_LIBRARY :
if (!path.isAbsolute()) {
return null;
}
return EGLCore.newLibraryEntry(path, sourceAttachmentPath,
sourceAttachmentRootPath, isExported);
case IEGLPathEntry.CPE_SOURCE :
// must be an entry in this project or specify another project
String projSegment = path.segment(0);
if (projSegment != null && projSegment.equals(projectName)) { // this project
// EGLMIGRATION: Inclusion Pattern support?
//return EGLCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation);
return EGLCore.newSourceEntry(path, exclusionPatterns, outputLocation);
} else { // another project
/*Is able to find the project in the workspace?
If yes, then return the project entry in the workspace;
If no, then try to find the project in the target platform,
and create the entry as a library entry which reference to an EGLAR!
*/
// if(isProjectExistedInWS(projSegment) ) {
return EGLCore.newProjectEntry(path, isExported);
// }
//if we get here, assume its going to be in the workspace
//One possible is that workspace cannot find the project, assume the
//project is not imported to the workspace.
// return EGLCore.newSourceEntry(path, exclusionPatterns, outputLocation);
}
case IEGLPathEntry.CPE_VARIABLE :
return EGLCore.newVariableEntry(
path,
sourceAttachmentPath,
sourceAttachmentRootPath,
isExported);
case IEGLPathEntry.CPE_CONTAINER :
return EGLCore.newContainerEntry(
path,
isExported);
case EGLPathEntry.K_OUTPUT :
if (!path.isAbsolute()) return null;
return new EGLPathEntry(
EGLPathEntry.K_OUTPUT,
IEGLPathEntry.CPE_LIBRARY,
path,
//EGLPathEntry.INCLUDE_ALL, // EGLMIGRATION: Inclusion Pattern support?
EGLPathEntry.EXCLUDE_NONE,
null, // source attachment
null, // source attachment root
null, // custom output location
false);
default :
throw new Assert.AssertionFailedException(EGLModelResources.bind(EGLModelResources.eglpathUnknownKind, kindAttr));
}
}
/**
* Check if the project is existed in the workspace.
* @param projSegment
* @return
*/
private static boolean isProjectExistedInWS(String projSegment) {
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IProject[] projects = workspaceRoot.getProjects();
for(IProject project : projects) {
if(projSegment.equals(project.getName())) {
return true;
}
}
return false;
}
/**
* Returns true if the given object is a eglpath entry
* with equivalent attributes.
*/
public boolean equals(Object object) {
if (this == object)
return true;
if (object instanceof IEGLPathEntry) {
IEGLPathEntry otherEntry = (IEGLPathEntry) object;
if (this.contentKind != otherEntry.getContentKind())
return false;
if (this.entryKind != otherEntry.getEntryKind())
return false;
if (this.isExported != otherEntry.isExported())
return false;
if(this.path == null) {
if(otherEntry.getPath() != null) {
return false;
}
} else {
if(otherEntry.getPath() == null) {
return false;
}
if (!this.path.toOSString().equalsIgnoreCase((otherEntry.getPath().toOSString())))
return false;
}
IPath[] otherExcludes = otherEntry.getExclusionPatterns();
if (this.exclusionPatterns != otherExcludes){
int excludeLength = this.exclusionPatterns.length;
if (otherExcludes.length != excludeLength)
return false;
for (int i = 0; i < excludeLength; i++) {
// compare toStrings instead of IPaths
// since IPath.equals is specified to ignore trailing separators
if (!this.exclusionPatterns[i].toString().equals(otherExcludes[i].toString()))
return false;
}
}
IPath otherPath = otherEntry.getOutputLocation();
if (this.specificOutputLocation == null) {
if (otherPath != null)
return false;
} else {
if (!this.specificOutputLocation.equals(otherPath))
return false;
}
otherPath = otherEntry.getSourceAttachmentPath();
if (this.sourceAttachmentPath == null) {
if (otherPath != null)
return false;
} else {
if (!this.sourceAttachmentPath.equals(otherPath))
return false;
}
return true;
} else {
return false;
}
}
/**
* @see IEGLPathEntry
*/
public int getContentKind() {
return this.contentKind;
}
/**
* @see IEGLPathEntry
*/
public int getEntryKind() {
return this.entryKind;
}
/**
* @see IEGLPathEntry#getExclusionPatterns()
*/
public IPath[] getExclusionPatterns() {
return this.exclusionPatterns;
}
/**
* @see IEGLPathEntry#getOutputLocation()
*/
public IPath getOutputLocation() {
return this.specificOutputLocation;
}
/**
* @see IEGLPathEntry
*/
public IPath getPath() {
return this.path;
}
/**
* @see IEGLPathEntry
*/
public IPath getSourceAttachmentPath() {
return this.sourceAttachmentPath;
}
/**
* @see IEGLPathEntry
*/
public IPath getSourceAttachmentRootPath() {
return this.sourceAttachmentRootPath;
}
/**
* Returns the hash code for this eglpath entry
*/
public int hashCode() {
return this.path.hashCode();
}
/**
* @see IEGLPathEntry#isExported()
*/
public boolean isExported() {
return this.isExported;
}
/**
* Returns the kind of a <code>PackageFragmentRoot</code> from its <code>String</code> form.
*/
static int kindFromString(String kindStr) {
if (kindStr.equalsIgnoreCase("prj")) //$NON-NLS-1$
return IEGLPathEntry.CPE_PROJECT;
if (kindStr.equalsIgnoreCase("var")) //$NON-NLS-1$
return IEGLPathEntry.CPE_VARIABLE;
if (kindStr.equalsIgnoreCase("con")) //$NON-NLS-1$
return IEGLPathEntry.CPE_CONTAINER;
if (kindStr.equalsIgnoreCase("src")) //$NON-NLS-1$
return IEGLPathEntry.CPE_SOURCE;
if (kindStr.equalsIgnoreCase("lib")) //$NON-NLS-1$
return IEGLPathEntry.CPE_LIBRARY;
if (kindStr.equalsIgnoreCase("output")) //$NON-NLS-1$
return EGLPathEntry.K_OUTPUT;
return -1;
}
/**
* Returns a <code>String</code> for the kind of a class path entry.
*/
static String kindToString(int kind) {
switch (kind) {
case IEGLPathEntry.CPE_PROJECT :
return "src"; // backward compatibility //$NON-NLS-1$
case IEGLPathEntry.CPE_SOURCE :
return "src"; //$NON-NLS-1$
case IEGLPathEntry.CPE_LIBRARY :
return "lib"; //$NON-NLS-1$
case IEGLPathEntry.CPE_VARIABLE :
return "var"; //$NON-NLS-1$
case IEGLPathEntry.CPE_CONTAINER :
return "con"; //$NON-NLS-1$
case EGLPathEntry.K_OUTPUT :
return "output"; //$NON-NLS-1$
default :
return "unknown"; //$NON-NLS-1$
}
}
/**
* Returns a printable representation of this eglpath entry.
*/
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(getPath().toString());
buffer.append('[');
switch (getEntryKind()) {
case IEGLPathEntry.CPE_LIBRARY :
buffer.append("CPE_LIBRARY"); //$NON-NLS-1$
break;
case IEGLPathEntry.CPE_PROJECT :
buffer.append("CPE_PROJECT"); //$NON-NLS-1$
break;
case IEGLPathEntry.CPE_SOURCE :
buffer.append("CPE_SOURCE"); //$NON-NLS-1$
break;
case IEGLPathEntry.CPE_VARIABLE :
buffer.append("CPE_VARIABLE"); //$NON-NLS-1$
break;
case IEGLPathEntry.CPE_CONTAINER :
buffer.append("CPE_CONTAINER"); //$NON-NLS-1$
break;
}
buffer.append("]["); //$NON-NLS-1$
switch (getContentKind()) {
case IPackageFragmentRoot.K_BINARY :
buffer.append("K_BINARY"); //$NON-NLS-1$
break;
case IPackageFragmentRoot.K_SOURCE :
buffer.append("K_SOURCE"); //$NON-NLS-1$
break;
case EGLPathEntry.K_OUTPUT :
buffer.append("K_OUTPUT"); //$NON-NLS-1$
break;
}
buffer.append(']');
if (getSourceAttachmentPath() != null) {
buffer.append("[sourcePath:"); //$NON-NLS-1$
buffer.append(getSourceAttachmentPath());
buffer.append(']');
}
if (getSourceAttachmentRootPath() != null) {
buffer.append("[rootPath:"); //$NON-NLS-1$
buffer.append(getSourceAttachmentRootPath());
buffer.append(']');
}
buffer.append("[isExported:"); //$NON-NLS-1$
buffer.append(this.isExported);
buffer.append(']');
IPath[] patterns = getExclusionPatterns();
int length;
if ((length = patterns.length) > 0) {
buffer.append("[excluding:"); //$NON-NLS-1$
for (int i = 0; i < length; i++) {
buffer.append(patterns[i]);
if (i != length-1) {
buffer.append('|');
}
}
buffer.append(']');
}
if (getOutputLocation() != null) {
buffer.append("[output:"); //$NON-NLS-1$
buffer.append(getOutputLocation());
buffer.append(']');
}
return buffer.toString();
}
public EGLPathEntry resolvedDotDot() {
IPath resolvedPath = resolveDotDot(this.path);
if (resolvedPath == this.path)
return this;
return new EGLPathEntry(
getContentKind(),
getEntryKind(),
resolvedPath,
// this.inclusionPatterns,
this.exclusionPatterns,
getSourceAttachmentPath(),
getSourceAttachmentRootPath(),
getOutputLocation(),
this.isExported);
}
/*
* Resolves the ".." in the given path. Returns the given path if it contains no ".." segment.
*/
public static IPath resolveDotDot(IPath path) {
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IPath newPath = null;
IPath workspaceLocation = null;
for (int i = 0, length = path.segmentCount(); i < length; i++) {
String segment = path.segment(i);
if (DOT_DOT.equals(segment)) {
if (newPath == null) {
if (i == 0) {
workspaceLocation = workspaceRoot.getLocation();
newPath = workspaceLocation;
} else {
newPath = path.removeFirstSegments(i);
}
} else {
if (newPath.segmentCount() > 0) {
newPath = newPath.removeLastSegments(1);
} else {
workspaceLocation = workspaceRoot.getLocation();
newPath = workspaceLocation;
}
}
} else if (newPath != null) {
if (newPath.equals(workspaceLocation) && workspaceRoot.getProject(segment).isAccessible()) {
newPath = new Path(segment).makeAbsolute();
} else {
newPath = newPath.append(segment);
}
}
}
if (newPath == null)
return path;
return newPath;
}
/**
* Answers an ID which is used to distinguish entries during package
* fragment root computations
*/
public String rootID(){
if (this.rootID == null) {
switch(this.entryKind){
case IEGLPathEntry.CPE_LIBRARY :
this.rootID = "[LIB]"+this.path; //$NON-NLS-1$
break;
case IEGLPathEntry.CPE_PROJECT :
this.rootID = "[PRJ]"+this.path; //$NON-NLS-1$
break;
case IEGLPathEntry.CPE_SOURCE :
this.rootID = "[SRC]"+this.path; //$NON-NLS-1$
break;
case IEGLPathEntry.CPE_VARIABLE :
this.rootID = "[VAR]"+this.path; //$NON-NLS-1$
break;
case IEGLPathEntry.CPE_CONTAINER :
this.rootID = "[CON]"+this.path; //$NON-NLS-1$
break;
default :
this.rootID = ""; //$NON-NLS-1$
break;
}
}
return this.rootID;
}
public boolean isBinaryProject() {
return isBinaryProject;
}
public void setBinaryProject(boolean isBinaryProject) {
this.isBinaryProject = isBinaryProject;
}
}