blob: 558967cb20e2cfebf80087db592fe35c53147e99 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2020 IBM Corporation and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0.
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# IBM Corporation - org.eclipse.jdt: initial API and implementation
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.buildpaths.ui;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IProject;
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.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.statet.jcommons.collections.ImCollection;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.ltk.buildpaths.core.BuildpathElement;
import org.eclipse.statet.ltk.buildpaths.core.BuildpathElementType;
import org.eclipse.statet.ltk.buildpaths.core.BuildpathInitializer;
import org.eclipse.statet.ltk.buildpaths.core.IBuildpathAttribute;
import org.eclipse.statet.ltk.buildpaths.core.IBuildpathElement;
public class BuildpathListElement {
private final BuildpathElementType type;
private final IProject project;
private IPath path;
private final IPath orginalPath;
private final IResource resource;
private boolean isMissing;
private Object parent;
private final ArrayList<Object> children;
private IPath linkTarget;
private final IPath orginalLinkTarget;
private IBuildpathElement cachedElement;
public BuildpathListElement(final Object parent, final IProject project, final BuildpathElementType type,
final IPath path, final IResource resource, final IPath linkTarget, final boolean newElement) {
this.type= type;
this.parent= parent;
this.children= new ArrayList<>();
this.project= project;
this.isMissing= false;
this.path= path;
this.orginalPath= (newElement) ? null : path;
this.linkTarget= linkTarget;
this.orginalLinkTarget= linkTarget;
this.resource= resource;
initBuiltinAttributes();
}
public BuildpathListElement(final IProject project, final Object parent,
final IBuildpathElement element, final boolean newElement) {
this.type= element.getType();
this.parent= parent;
this.children= new ArrayList<>();
this.project= project;
this.isMissing= false;
{ IPath path= element.getPath();
final IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
// get the resource
IResource resource= null;
IPath linkTarget= null;
switch (element.getType().getName()) {
case IBuildpathElement.SOURCE:
path= path.removeTrailingSeparator();
resource= root.findMember(path);
if (resource == null) {
if (root.getWorkspace().validatePath(path.toString(), IResource.FOLDER).isOK()) {
resource= root.getFolder(path);
}
this.isMissing= true;
}
else if (resource.isLinked()) {
linkTarget= resource.getLocation();
}
break;
case IBuildpathElement.PROJECT:
resource= root.findMember(path);
this.isMissing= (resource == null);
break;
default:
throw new UnsupportedOperationException(element.getType().getName());
}
this.path= path;
this.orginalPath= (newElement) ? null : path;
this.resource= resource;
this.linkTarget= linkTarget;
this.orginalLinkTarget= linkTarget;
}
initBuiltinAttributes();
setAttribute(IBuildpathAttribute.FILTER_INCLUSIONS, element.getInclusionPatterns());
setAttribute(IBuildpathAttribute.FILTER_EXCLUSIONS, element.getExclusionPatterns());
setAttribute(IBuildpathAttribute.SOURCE_ATTACHMENT, element.getSourceAttachmentPath());
setAttribute(IBuildpathAttribute.OUTPUT, element.getOutputPath());
final ImList<IBuildpathAttribute> extraAttributes= element.getExtraAttributes();
for (final IBuildpathAttribute attribute : extraAttributes) {
if (setAttribute(attribute.getName(), attribute.getValue()) == null) {
addExtraAttribute(attribute.getName(), attribute.getValue());
}
}
}
/**
* Gets the class path entry kind.
* @return the entry kind
* @see IBuildpathElement#getType()
*/
public BuildpathElementType getType() {
return this.type;
}
/**
* Entries without resource are either non existing or a variable entry
* External jars do not have a resource
* @return returns the resource
*/
public IResource getResource() {
return this.resource;
}
/**
* Gets the project.
* @return Returns a IProject
*/
public IProject getProject() {
return this.project;
}
/**
* Gets the class path entry path.
* @return returns the path
* @see IBuildpathElement#getPath()
*/
public IPath getPath() {
return this.path;
}
public IPath getOrginalPath() {
return this.orginalPath;
}
public void setPath(final IPath path) {
this.cachedElement= null;
this.path= path;
}
public IPath getLinkTarget() {
return this.linkTarget;
}
public IPath getOrginalLinkTarget() {
return this.orginalLinkTarget;
}
public void setLinkTarget(final IPath linkTarget) {
this.cachedElement= null;
this.linkTarget= linkTarget;
}
public IBuildpathElement getCoreElement() {
if (this.cachedElement == null) {
this.cachedElement= newCoreElement();
}
return this.cachedElement;
}
private IBuildpathElement newCoreElement() {
return new BuildpathElement(this.type, this.path,
(this.type.isAttributeBuiltin(IBuildpathAttribute.FILTER_INCLUSIONS)) ?
(ImList<IPath>) getAttributeValue(IBuildpathAttribute.FILTER_INCLUSIONS) : null,
(this.type.isAttributeBuiltin(IBuildpathAttribute.FILTER_EXCLUSIONS)) ?
(ImList<IPath>) getAttributeValue(IBuildpathAttribute.FILTER_EXCLUSIONS) : null,
(this.type.isAttributeBuiltin(IBuildpathAttribute.SOURCE_ATTACHMENT)) ?
(IPath) getAttributeValue(IBuildpathAttribute.SOURCE_ATTACHMENT) : null,
null,
(this.type.isAttributeBuiltin(IBuildpathAttribute.OUTPUT)) ?
(IPath) getAttributeValue(IBuildpathAttribute.OUTPUT) : null,
null,
false,
ImCollections.toList(getExtraAttributes()) );
}
public List<Object> getChildren() {
return getFilteredChildren(ImCollections.<String>emptySet());
}
private void initBuiltinAttributes() {
for (final String attributeKey : this.type.getAttributeBuiltinKeys()) {
switch (attributeKey) {
case IBuildpathAttribute.FILTER_INCLUSIONS:
case IBuildpathAttribute.FILTER_EXCLUSIONS:
createAttribute(attributeKey, ImCollections.emptyList(), true);
continue;
case IBuildpathAttribute.OUTPUT:
createAttribute(attributeKey, null, true);
continue;
default:
throw new IllegalStateException(attributeKey);
}
}
}
private List<IBuildpathAttribute> getExtraAttributes() {
final ArrayList<IBuildpathAttribute> list= new ArrayList<>(Math.min(this.children.size(), 8));
for (final Object child : this.children) {
if (child instanceof BuildpathListElementAttribute) {
final BuildpathListElementAttribute attribute= (BuildpathListElementAttribute) child;
if (!attribute.isBuiltin() && attribute.getValue() != null) {
list.add(attribute.getCoreAttribute());
}
}
}
return list;
}
private boolean isFiltered(final Object child, final ImCollection<String> filteredAttributeKeys) {
if (child instanceof BuildpathListElementAttribute) {
final BuildpathListElementAttribute attribute= (BuildpathListElementAttribute) child;
if (filteredAttributeKeys.contains(attribute.getName())) {
return true;
}
if (attribute.getStatus().getCode() == BuildpathInitializer.NOT_SUPPORTED) {
return true;
}
if (!attribute.isBuiltin()) {
// return !BuildpathsUtils.getDefault().getClasspathAttributeConfigurationDescriptors().containsKey(key);
}
}
return false;
}
public List<Object> getFilteredChildren(final ImCollection<String> filteredAttributeKeys) {
final int nChildren= this.children.size();
final ArrayList<Object> filtered= new ArrayList<>(nChildren);
for (int i= 0; i < nChildren; i++) {
final Object child= this.children.get(i);
if (!isFiltered(child, filteredAttributeKeys)) {
filtered.add(child);
}
}
return filtered;
}
public BuildpathListElementAttribute findAttribute(final String name) {
for (final Object child : this.children) {
if (child instanceof BuildpathListElementAttribute) {
final BuildpathListElementAttribute attribute= (BuildpathListElementAttribute) child;
if (name.equals(attribute.getName())) {
return attribute;
}
}
}
return null;
}
public Object getAttributeValue(final String name) {
final BuildpathListElementAttribute attribute= findAttribute(name);
if (attribute != null) {
return attribute.getValue();
}
return null;
}
public List<BuildpathListElementAttribute> getAllAttributes() {
final ArrayList<BuildpathListElementAttribute> attributes= new ArrayList<>();
for (final Object child : this.children) {
if (child instanceof BuildpathListElementAttribute) {
attributes.add((BuildpathListElementAttribute) child);
}
}
return attributes;
}
/**
* Notifies that an attribute has changed
*
* @param name name of changed attribute
*/
protected void attributeChanged(final String name) {
this.cachedElement= null;
}
public BuildpathListElementAttribute setAttribute(final String name, final Object value) {
final BuildpathListElementAttribute attribute= findAttribute(name);
if (attribute == null) {
return null;
}
switch(name) {
case IBuildpathAttribute.FILTER_INCLUSIONS:
case IBuildpathAttribute.FILTER_EXCLUSIONS:
if (!(value instanceof ImList<?>)) {
throw new IllegalArgumentException();
}
break;
default:
break;
}
attribute.setValue(value);
return attribute;
}
private BuildpathListElementAttribute createAttribute(final String name,
final Object value, final boolean builtin) {
final BuildpathListElementAttribute attribute= new BuildpathListElementAttribute(this,
name, value, builtin );
this.children.add(attribute);
return attribute;
}
public void setAttributesFromExisting(final BuildpathListElement existing) {
Assert.isTrue(existing.getType() == getType());
final List<BuildpathListElementAttribute> attributes= existing.getAllAttributes();
for (final BuildpathListElementAttribute attribute : attributes) {
final BuildpathListElementAttribute elem= findAttribute(attribute.getName());
if (elem == null) {
createAttribute(attribute.getName(), attribute.getValue(), false);
}
else {
elem.setValue(attribute.getValue());
}
}
}
public BuildpathListElementAttribute addExtraAttribute(final String name, final Object value) {
return createAttribute(name, value, false);
}
public Object getParent() {
return this.parent;
}
/**
* Sets the parent container.
*
* @param parent the parent container
* @since 3.7
*/
void setParentContainer(final Object parent) {
this.parent= parent;
}
private IStatus evaluateContainerChildStatus(final BuildpathListElementAttribute attrib) {
return null;
}
public IStatus getContainerChildStatus(final BuildpathListElementAttribute attrib) {
if (this.parent instanceof BuildpathListElement) {
final BuildpathListElement parentElement= (BuildpathListElement) this.parent;
return parentElement.getContainerChildStatus(attrib);
}
return Status.OK_STATUS;
}
public boolean isInContainer(final String containerName) {
if (this.parent instanceof BuildpathListElement) {
final BuildpathListElement elem= (BuildpathListElement) this.parent;
return new Path(containerName).isPrefixOf(elem.getPath());
}
return false;
}
/**
* Returns if a entry is missing.
* @return Returns a boolean
*/
public boolean isMissing() {
return this.isMissing;
}
/**
* Returns if a entry has children that are missing
* @return Returns a boolean
*/
public boolean hasMissingChildren() {
for (final Object child : this.children) {
if (child instanceof BuildpathListElement
&& ((BuildpathListElement) child).isMissing()) {
return true;
}
}
return false;
}
/**
* Sets the 'missing' state of the entry.
* @param isMissing the new state
*/
public void setIsMissing(final boolean isMissing) {
this.isMissing= isMissing;
}
public boolean isDeprecated() {
return false;
}
public String getDeprecationMessage() {
return null;
}
@Override
public int hashCode() {
return this.type.hashCode() + this.path.hashCode();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj.getClass().equals(getClass())) {
final BuildpathListElement other= (BuildpathListElement) obj;
return getCoreElement().equals(other.getCoreElement());
}
return false;
}
@Override
public String toString() {
return getCoreElement().toString();
}
}