blob: 6754d67bc4c54914e79503ca5dbb7ba1ffcbe8d2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 2020 1C-Soft LLC 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:
* Vladimir Piskarev (1C) - initial API and implementation
*******************************************************************************/
package org.eclipse.handly.model.impl;
import static org.eclipse.handly.context.Contexts.of;
import static org.eclipse.handly.context.Contexts.with;
import static org.eclipse.handly.util.ToStringOptions.FORMAT_STYLE;
import java.lang.reflect.Array;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.handly.context.IContext;
import org.eclipse.handly.model.Elements;
import org.eclipse.handly.model.IElement;
import org.eclipse.handly.model.IModel;
import org.eclipse.handly.util.ICollector;
import org.eclipse.handly.util.ToStringOptions.FormatStyle;
/**
* All {@link IElement}s must implement this interface.
*
* @noextend This interface is not intended to be extended by clients.
*/
public interface IElementImpl
extends IElement
{
/**
* Returns the name of this element, or <code>null</code>
* if this element has no name. This is a handle-only method.
*
* @return the element name, or <code>null</code> if this element has no name
*/
String getName_();
/**
* Returns the element directly containing this element,
* or <code>null</code> if this element has no parent.
* This is a handle-only method.
*
* @return the parent element, or <code>null</code> if this element has
* no parent
*/
IElement getParent_();
/**
* Returns the root element containing this element.
* Returns this element if it has no parent.
* This is a handle-only method.
*
* @return the root element (never <code>null</code>)
*/
default IElement getRoot_()
{
IElement parent = getParent_();
if (parent == null)
return this;
else
return Elements.getRoot(parent);
}
/**
* Returns whether this element is equal to the given element and belongs
* to the same parent chain as the given element. This is a handle-only
* method.
* <p>
* This implementation accounts for the most general case where equal
* elements may belong to different parent chains. For example, in JDT, equal
* JarPackageFragmentRoots may belong to different Java projects. Specific
* models can provide an optimized implementation. For example, it would be
* possible to just return <code>equals(other)</code> if it were known for
* a model that equal elements might not belong to different parent chains.
* </p>
*
* @param other may be <code>null</code>
* @return <code>true</code> if this element is equal to the given element
* and belongs to the same parent chain, and <code>false</code> otherwise
*/
default boolean equalsAndSameParentChain_(IElement other)
{
if (this == other)
return true;
if (!equals(other))
return false;
IElement parent = getParent_();
IElement otherParent = Elements.getParent(other);
if (parent == null)
return otherParent == null;
return Elements.equalsAndSameParentChain(parent, otherParent);
}
/**
* Returns the model that owns this element. This is a handle-only method.
*
* @return the element's model (never <code>null</code>)
*/
IModel getModel_();
/**
* Returns a string representation of this element handle that can be used
* to recreate the handle via the model's {@link
* org.eclipse.handly.model.IElementHandleFactory IElementHandleFactory}.
* The format of the string is not specified, but the representation is
* stable across workbench sessions. This is a handle-only method.
*
* @return the handle memento for this element, or <code>null</code>
* if this element is unable to provide a handle memento
*/
default String getHandleMemento_()
{
return null;
}
/**
* Returns the innermost resource enclosing this element, or <code>null</code>
* if this element is not enclosed in a workspace resource.
* This is a handle-only method.
* <p>
* Note that it is safe to call this method and test the return value
* for <code>null</code> even when <code>org.eclipse.core.resources</code>
* bundle is not available.
* </p>
* <p>
* Since 1.3, this method is no longer abstract. The default implementation
* always returns <code>null</code>.
* </p>
*
* @return the innermost resource enclosing this element, or <code>null</code>
* if this element is not enclosed in a workspace resource
*/
default IResource getResource_()
{
return null;
}
/**
* Returns a file system location for this element. The resulting URI is
* suitable to passing to <code>EFS.getStore(URI)</code>. Returns
* <code>null</code> if no location can be determined.
*
* @return a file system location for this element,
* or <code>null</code> if no location can be determined
*/
default URI getLocationUri_()
{
IResource resource = getResource_();
if (resource != null)
return resource.getLocationURI();
return null;
}
/**
* Returns whether this element exists in the model.
* <p>
* Handles may or may not be backed by an actual element. Handles that are
* backed by an actual element are said to "exist".
* </p>
*
* @return <code>true</code> if this element exists in the model, and
* <code>false</code> if this element does not exist
*/
boolean exists_();
/**
* Returns the immediate children of this element. Unless otherwise specified
* by the implementing element, the children are in no particular order.
*
* @param context the operation context (not <code>null</code>)
* @param monitor a progress monitor, or <code>null</code>
* if progress reporting is not desired. The caller must not rely on
* {@link IProgressMonitor#done()} having been called by the receiver
* @return the immediate children of this element (never <code>null</code>).
* Clients <b>must not</b> modify the returned array.
* @throws CoreException if this element does not exist or if an
* exception occurs while accessing its corresponding resource
*/
IElement[] getChildren_(IContext context, IProgressMonitor monitor)
throws CoreException;
/**
* Returns the immediate children of this element that have the given type.
* Unless otherwise specified by the implementing element, the children
* are in no particular order.
*
* @param type not <code>null</code>
* @param context the operation context (not <code>null</code>)
* @param monitor a progress monitor, or <code>null</code>
* if progress reporting is not desired. The caller must not rely on
* {@link IProgressMonitor#done()} having been called by the receiver
* @return the immediate children of this element that have the given type
* (never <code>null</code>). Clients <b>must not</b> modify the returned
* array.
* @throws CoreException if this element does not exist or if an
* exception occurs while accessing its corresponding resource
*/
default <T> T[] getChildrenOfType_(Class<T> type, IContext context,
IProgressMonitor monitor) throws CoreException
{
IElement[] children = getChildren_(context, monitor);
if (type.isAssignableFrom(children.getClass().getComponentType()))
{
@SuppressWarnings("unchecked")
T[] result = (T[])children;
return result;
}
ArrayList<T> list = new ArrayList<T>(children.length);
for (IElement child : children)
{
if (type.isInstance(child))
list.add(type.cast(child));
}
@SuppressWarnings("unchecked")
T[] result = (T[])Array.newInstance(type, list.size());
return list.toArray(result);
}
/**
* Adds the immediate children of this element to the given collector.
* Unless otherwise specified by the implementing element, the children are
* added in no particular order.
*
* @param context the operation context (not <code>null</code>)
* @param collector the element collector (not <code>null</code>)
* @param monitor a progress monitor, or <code>null</code>
* if progress reporting is not desired. The caller must not rely on
* {@link IProgressMonitor#done()} having been called by the receiver
* @throws CoreException if this element does not exist or if an
* exception occurs while accessing its corresponding resource
* @throws OperationCanceledException if this method is canceled
* @since 1.5
*/
default void fetchChildren_(IContext context,
ICollector<? super IElement> collector, IProgressMonitor monitor)
throws CoreException
{
collector.addAll(Arrays.asList(getChildren_(context, monitor)));
}
/**
* Adds the immediate children of this element that have the given type
* to the given collector. Unless otherwise specified by the implementing
* element, the children are added in no particular order.
*
* @param type not <code>null</code>
* @param context the operation context (not <code>null</code>)
* @param collector the element collector (not <code>null</code>)
* @param monitor a progress monitor, or <code>null</code>
* if progress reporting is not desired. The caller must not rely on
* {@link IProgressMonitor#done()} having been called by the receiver
* @throws CoreException if this element does not exist or if an
* exception occurs while accessing its corresponding resource
* @throws OperationCanceledException if this method is canceled
* @since 1.5
*/
default <T> void fetchChildrenOfType_(Class<T> type, IContext context,
ICollector<? super T> collector, IProgressMonitor monitor)
throws CoreException
{
collector.addAll(Arrays.asList(getChildrenOfType_(type, context,
monitor)));
}
/**
* Returns a string representation of this element in a form suitable for
* debugging purposes. Clients can influence the result with format options
* specified in the given context; unrecognized options are ignored and
* an empty context is permitted.
* <p>
* Implementations are encouraged to support common options defined in
* {@link org.eclipse.handly.util.ToStringOptions ToStringOptions} and
* interpret the {@link org.eclipse.handly.util.ToStringOptions#FORMAT_STYLE
* FORMAT_STYLE} as follows:
* </p>
* <ul>
* <li>{@link org.eclipse.handly.util.ToStringOptions.FormatStyle#FULL FULL}
* - A full representation that lists ancestors and children.</li>
* <li>{@link org.eclipse.handly.util.ToStringOptions.FormatStyle#LONG LONG}
* - A long representation that lists children but not ancestors.</li>
* <li>{@link org.eclipse.handly.util.ToStringOptions.FormatStyle#MEDIUM MEDIUM}
* - A compact representation that lists ancestors but not children.</li>
* <li>{@link org.eclipse.handly.util.ToStringOptions.FormatStyle#SHORT SHORT}
* - A minimal representation that does not list ancestors or children.</li>
* </ul>
*
* @param context not <code>null</code>
* @return a string representation of this element (never <code>null</code>)
*/
String toString_(IContext context);
/**
* Returns a string representation of this element in a form suitable for
* displaying to the user, e.g., in message dialogs. Clients can influence
* the result with format options specified in the given context;
* unrecognized options are ignored and an empty context is permitted.
* <p>
* Implementations are encouraged to support common options defined in
* {@link org.eclipse.handly.util.ToStringOptions ToStringOptions} and may
* interpret the {@link org.eclipse.handly.util.ToStringOptions#FORMAT_STYLE
* FORMAT_STYLE} as they see fit in a way that is specific to the model.
* No hard rules apply, but usually the string representation does not list
* the element's children regardless of the format style, and a {@link
* org.eclipse.handly.util.ToStringOptions.FormatStyle#FULL FULL}
* representation fully identifies the element within the model.
* </p>
*
* @param context not <code>null</code>
* @return a string representation of this element (never <code>null</code>)
*/
default String toDisplayString_(IContext context)
{
FormatStyle style = context.getOrDefault(FORMAT_STYLE);
if (style != FormatStyle.SHORT && style != FormatStyle.MEDIUM)
context = with(of(FORMAT_STYLE, FormatStyle.MEDIUM), context);
return toString_(context);
}
}