| /******************************************************************************* |
| * Copyright (c) 2009, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.pde.api.tools.ui.internal.actions; |
| |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.pde.api.tools.internal.ApiBaselineManager; |
| import org.eclipse.pde.api.tools.internal.provisional.ISession; |
| import org.eclipse.pde.api.tools.internal.provisional.ITreeModel; |
| import org.eclipse.pde.api.tools.internal.provisional.ITreeNode; |
| import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers; |
| import org.eclipse.pde.api.tools.internal.provisional.comparator.ApiComparator; |
| import org.eclipse.pde.api.tools.internal.provisional.comparator.DeltaVisitor; |
| import org.eclipse.pde.api.tools.internal.provisional.comparator.IDelta; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiType; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot; |
| import org.eclipse.pde.api.tools.internal.util.Util; |
| import org.eclipse.ui.views.properties.IPropertyDescriptor; |
| import org.eclipse.ui.views.properties.IPropertySheetEntry; |
| import org.eclipse.ui.views.properties.IPropertySource; |
| import org.eclipse.ui.views.properties.IPropertySource2; |
| import org.eclipse.ui.views.properties.PropertyDescriptor; |
| |
| import com.ibm.icu.text.DateFormat; |
| |
| public class DeltaSession implements ISession { |
| static Object[] NO_CHILDREN = new Object[0]; |
| |
| static class TreeNode implements ITreeNode, IPropertySource2, IAdaptable { |
| private static final IPropertyDescriptor[] NO_PROPERTY_DESCRIPTORS = new IPropertyDescriptor[0]; |
| // Property id keys |
| public static final String ID_MESSAGE = "IDelta.Message"; //$NON-NLS-1$ |
| public static final String ID_COMPONENT = "IDelta.Component"; //$NON-NLS-1$ |
| public static final String ID_ELEMENT_TYPE = "IDelta.ElementType"; //$NON-NLS-1$ |
| public static final String ID_FLAGS = "IDelta.Flags"; //$NON-NLS-1$ |
| private static final Object ID_KEY = "IDelta.Key"; //$NON-NLS-1$ |
| private static final Object ID_KIND = "IDelta.Kind"; //$NON-NLS-1$ |
| private static final Object ID_NEW_MODIFIERS = "IDelta.NewModifiers"; //$NON-NLS-1$ |
| private static final Object ID_OLD_MODIFIERS = "IDelta.OldModifiers"; //$NON-NLS-1$ |
| private static final Object ID_CURRENT_RESTRICTIONS = "IDelta.CurrentRestrictions"; //$NON-NLS-1$ |
| private static final Object ID_PREVIOUS_RESTRICTIONS = "IDelta.PreviousRestrictions"; //$NON-NLS-1$ |
| private static final Object ID_TYPENAME = "IDelta.TypeName"; //$NON-NLS-1$ |
| |
| // categories |
| public static final String P_MESSAGE_CATEGORY = ActionMessages.MessageCategory; |
| public static final String P_INFO_CATEGORY = ActionMessages.InfoCategory; |
| |
| public static final String P_MESSAGE = ActionMessages.PropertyMessageKey; |
| public static final String P_COMPONENT = ActionMessages.PropertyComponentKey; |
| public static final String P_ELEMENT_TYPE = ActionMessages.PropertyElementTypeKey; |
| public static final String P_FLAGS = ActionMessages.PropertyFlagsKey; |
| public static final String P_KEY = ActionMessages.PropertyKeyKey; |
| public static final String P_KIND = ActionMessages.PropertyKindKey; |
| public static final String P_NEW_MODIFIERS = ActionMessages.PropertyNewModifiersKey; |
| public static final String P_OLD_MODIFIERS = ActionMessages.PropertyOldModifiersKey; |
| public static final String P_CURRENT_RESTRICTIONS = ActionMessages.PropertyCurrentRestrictionsKey; |
| public static final String P_PREVIOUS_RESTRICTIONS = ActionMessages.PropertyPreviousRestrictionsKey; |
| public static final String P_TYPENAME = ActionMessages.PropertyTypeNameKey; |
| |
| private static List<PropertyDescriptor> Descriptors; |
| static { |
| Descriptors = new ArrayList<>(); |
| |
| PropertyDescriptor propertyDescriptor = new PropertyDescriptor(ID_MESSAGE, P_MESSAGE); |
| propertyDescriptor.setCategory(P_MESSAGE_CATEGORY); |
| Descriptors.add(propertyDescriptor); |
| |
| propertyDescriptor = new PropertyDescriptor(ID_COMPONENT, P_COMPONENT); |
| propertyDescriptor.setCategory(P_INFO_CATEGORY); |
| Descriptors.add(propertyDescriptor); |
| |
| propertyDescriptor = new PropertyDescriptor(ID_KEY, P_KEY); |
| propertyDescriptor.setCategory(P_INFO_CATEGORY); |
| propertyDescriptor.setFilterFlags(new String[] { IPropertySheetEntry.FILTER_ID_EXPERT }); |
| Descriptors.add(propertyDescriptor); |
| |
| propertyDescriptor = new PropertyDescriptor(ID_KIND, P_KIND); |
| propertyDescriptor.setCategory(P_INFO_CATEGORY); |
| Descriptors.add(propertyDescriptor); |
| |
| propertyDescriptor = new PropertyDescriptor(ID_NEW_MODIFIERS, P_NEW_MODIFIERS); |
| propertyDescriptor.setCategory(P_INFO_CATEGORY); |
| Descriptors.add(propertyDescriptor); |
| |
| propertyDescriptor = new PropertyDescriptor(ID_OLD_MODIFIERS, P_OLD_MODIFIERS); |
| propertyDescriptor.setCategory(P_INFO_CATEGORY); |
| Descriptors.add(propertyDescriptor); |
| |
| propertyDescriptor = new PropertyDescriptor(ID_CURRENT_RESTRICTIONS, P_CURRENT_RESTRICTIONS); |
| propertyDescriptor.setCategory(P_INFO_CATEGORY); |
| Descriptors.add(propertyDescriptor); |
| |
| propertyDescriptor = new PropertyDescriptor(ID_PREVIOUS_RESTRICTIONS, P_PREVIOUS_RESTRICTIONS); |
| propertyDescriptor.setCategory(P_INFO_CATEGORY); |
| Descriptors.add(propertyDescriptor); |
| |
| propertyDescriptor = new PropertyDescriptor(ID_TYPENAME, P_TYPENAME); |
| propertyDescriptor.setCategory(P_INFO_CATEGORY); |
| Descriptors.add(propertyDescriptor); |
| |
| propertyDescriptor = new PropertyDescriptor(ID_FLAGS, P_FLAGS); |
| propertyDescriptor.setCategory(P_INFO_CATEGORY); |
| Descriptors.add(propertyDescriptor); |
| |
| propertyDescriptor = new PropertyDescriptor(ID_ELEMENT_TYPE, P_ELEMENT_TYPE); |
| propertyDescriptor.setCategory(P_INFO_CATEGORY); |
| Descriptors.add(propertyDescriptor); |
| } |
| |
| /** |
| * Returns the descriptors |
| */ |
| static List<PropertyDescriptor> getDescriptors() { |
| return Descriptors; |
| } |
| |
| Map<String, TreeNode> children; |
| String name; |
| DeltaSession.TreeNode parent; |
| Object data; |
| int id; |
| |
| public TreeNode(int id, String name, Object data) { |
| this.name = name; |
| this.id = id; |
| this.data = data; |
| } |
| |
| @Override |
| public Object[] getChildren() { |
| if (this.children == null) { |
| return NO_CHILDREN; |
| } |
| return this.children.values().toArray(new Object[this.children.size()]); |
| } |
| |
| public DeltaSession.TreeNode getNode(String name) { |
| if (this.children == null) { |
| return null; |
| } |
| return this.children.get(name); |
| } |
| |
| @Override |
| public int getId() { |
| return this.id; |
| } |
| |
| public void add(DeltaSession.TreeNode node) { |
| if (this.children == null) { |
| this.children = new LinkedHashMap<>(); |
| } |
| this.children.put(node.name, node); |
| } |
| |
| @Override |
| public boolean hasChildren() { |
| return this.children != null && !this.children.isEmpty(); |
| } |
| |
| @Override |
| public String toString() { |
| return String.valueOf(this.name); |
| } |
| |
| @Override |
| public Object getData() { |
| return this.data; |
| } |
| |
| @Override |
| public Object getEditableValue() { |
| return this; |
| } |
| |
| @Override |
| public IPropertyDescriptor[] getPropertyDescriptors() { |
| if (this.data != null) { |
| return getDescriptors().toArray(new IPropertyDescriptor[getDescriptors().size()]); |
| } |
| return NO_PROPERTY_DESCRIPTORS; |
| } |
| |
| @Override |
| public Object getPropertyValue(Object propKey) { |
| if (this.data == null) { |
| return null; |
| } |
| IDelta delta = (IDelta) this.data; |
| if (ID_MESSAGE.equals(propKey)) { |
| return delta.getMessage(); |
| } |
| if (ID_COMPONENT.equals(propKey)) { |
| return delta.getComponentVersionId(); |
| } |
| if (ID_ELEMENT_TYPE.equals(propKey)) { |
| int elementType = delta.getElementType(); |
| StringBuilder buffer = new StringBuilder(Util.getDeltaElementType(elementType)); |
| buffer.append(" (").append(elementType).append(')'); //$NON-NLS-1$ |
| return String.valueOf(buffer); |
| } |
| if (ID_FLAGS.equals(propKey)) { |
| int flags = delta.getFlags(); |
| StringBuilder buffer = new StringBuilder(Util.getDeltaFlagsName(flags)); |
| buffer.append(" (").append(flags).append(')'); //$NON-NLS-1$ |
| return String.valueOf(buffer); |
| } |
| if (ID_KEY.equals(propKey)) { |
| return delta.getKey(); |
| } |
| if (ID_KIND.equals(propKey)) { |
| int kind = delta.getKind(); |
| StringBuilder buffer = new StringBuilder(Util.getDeltaKindName(kind)); |
| buffer.append(" (").append(kind).append(')'); //$NON-NLS-1$ |
| return String.valueOf(buffer); |
| } |
| if (ID_NEW_MODIFIERS.equals(propKey)) { |
| return getDisplayedModifiers(delta.getNewModifiers()); |
| } |
| if (ID_OLD_MODIFIERS.equals(propKey)) { |
| return getDisplayedModifiers(delta.getOldModifiers()); |
| } |
| if (ID_CURRENT_RESTRICTIONS.equals(propKey)) { |
| int restrictions = delta.getCurrentRestrictions(); |
| return getDisplayRestrictions(restrictions); |
| } |
| if (ID_PREVIOUS_RESTRICTIONS.equals(propKey)) { |
| int restrictions = delta.getPreviousRestrictions(); |
| return getDisplayRestrictions(restrictions); |
| } |
| if (ID_TYPENAME.equals(propKey)) { |
| return delta.getTypeName(); |
| } |
| return null; |
| } |
| |
| private Object getDisplayRestrictions(int restrictions) { |
| StringBuilder buffer = new StringBuilder(RestrictionModifiers.getRestrictionText(restrictions)); |
| buffer.append(" (0x").append(Integer.toHexString(restrictions)).append(')'); //$NON-NLS-1$ |
| return String.valueOf(buffer); |
| } |
| |
| @Override |
| public boolean isPropertySet(Object id) { |
| return false; |
| } |
| |
| @Override |
| public void resetPropertyValue(Object id) { |
| // nothing to do |
| } |
| |
| @Override |
| public void setPropertyValue(Object id, Object value) { |
| // nothing to do |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter(Class<T> adapter) { |
| if (adapter == IPropertySource.class) { |
| return (T) this; |
| } |
| return null; |
| } |
| |
| private static String getDisplayedModifiers(int newModifiers) { |
| StringBuilder buffer = new StringBuilder(); |
| if (newModifiers == 0) { |
| buffer.append(ActionMessages.PropertyPackageVisibility); |
| } else { |
| if (Flags.isAbstract(newModifiers)) { |
| buffer.append("abstract"); //$NON-NLS-1$ |
| } |
| String separator = " | "; //$NON-NLS-1$ |
| if (Flags.isFinal(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("final"); //$NON-NLS-1$ |
| } |
| if (Flags.isNative(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("native"); //$NON-NLS-1$ |
| } |
| if (Flags.isPrivate(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("private"); //$NON-NLS-1$ |
| } |
| if (Flags.isProtected(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("protected"); //$NON-NLS-1$ |
| } |
| if (Flags.isPublic(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("public"); //$NON-NLS-1$ |
| } |
| if (Flags.isStatic(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("static"); //$NON-NLS-1$ |
| } |
| if (Flags.isStrictfp(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("strictfp"); //$NON-NLS-1$ |
| } |
| if (Flags.isSynchronized(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("synchronized"); //$NON-NLS-1$ |
| } |
| if (Flags.isTransient(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("transient"); //$NON-NLS-1$ |
| } |
| if (Flags.isVolatile(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("volatile"); //$NON-NLS-1$ |
| } |
| if (Flags.isVarargs(newModifiers)) { |
| if (buffer.length() > 0) { |
| buffer.append(separator); |
| } |
| buffer.append("vargars"); //$NON-NLS-1$ |
| } |
| } |
| buffer.append(" (0x").append(Integer.toHexString(newModifiers)).append(')'); //$NON-NLS-1$ |
| return buffer.toString(); |
| } |
| |
| @Override |
| public boolean isPropertyResettable(Object id) { |
| return false; |
| } |
| } |
| |
| static class TreeModel implements ITreeModel { |
| DeltaSession.TreeNode root; |
| |
| TreeModel(DeltaSession.TreeNode root) { |
| this.root = root; |
| } |
| |
| @Override |
| public ITreeNode getRoot() { |
| return this.root; |
| } |
| } |
| |
| IDelta delta; |
| String baselineName; |
| String timestamp; |
| String description; |
| |
| public DeltaSession(String description, IDelta delta, String baselineName) { |
| this.delta = delta; |
| this.baselineName = baselineName; |
| this.timestamp = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(new Date(System.currentTimeMillis())); |
| this.description = description; |
| } |
| |
| @Override |
| public ITreeModel getModel() { |
| DeltaSession.TreeNode root = new TreeNode(0, null, this.delta); |
| DeltaSession.TreeModel model = new TreeModel(root); |
| class TreeBuilder extends DeltaVisitor { |
| DeltaSession.TreeNode node; |
| |
| public TreeBuilder(DeltaSession.TreeNode node) { |
| this.node = node; |
| } |
| |
| @Override |
| public void endVisit(IDelta delta) { |
| if (delta.getChildren().length == 0) { |
| String typeName = delta.getTypeName(); |
| if (typeName == null) { |
| this.node.add(new TreeNode(0, delta.getKey(), delta)); |
| } else if (typeName.length() == 0) { |
| this.node.add(new TreeNode(0, delta.getMessage(), delta)); |
| } else { |
| // split the type name (package - type) |
| int index = typeName.lastIndexOf('.'); |
| String packageName = "<default package>"; //$NON-NLS-1$ |
| String actualTypeName = null; |
| if (index != -1) { |
| packageName = typeName.substring(0, index); |
| actualTypeName = typeName.substring(index + 1); |
| } else { |
| actualTypeName = typeName; |
| } |
| DeltaSession.TreeNode node2 = this.node.getNode(packageName); |
| if (node2 == null) { |
| node2 = new TreeNode(ITreeNode.PACKAGE, packageName, null); |
| this.node.add(node2); |
| } |
| DeltaSession.TreeNode node3 = node2.getNode(actualTypeName); |
| if (node3 == null) { |
| int id = 0; |
| switch (delta.getElementType()) { |
| case IDelta.ANNOTATION_ELEMENT_TYPE: |
| id = ITreeNode.ANNOTATION; |
| break; |
| case IDelta.INTERFACE_ELEMENT_TYPE: |
| id = ITreeNode.INTERFACE; |
| break; |
| case IDelta.CLASS_ELEMENT_TYPE: |
| id = ITreeNode.CLASS; |
| break; |
| case IDelta.ENUM_ELEMENT_TYPE: |
| id = ITreeNode.ENUM; |
| break; |
| default: |
| // we need to retrieve the type kind |
| try { |
| String componentVersionId = delta.getComponentVersionId(); |
| if (componentVersionId != null) { |
| int modifiers = retrieveTypeModifiers(delta, typeName, componentVersionId); |
| if (Flags.isEnum(modifiers)) { |
| id = ITreeNode.ENUM; |
| } else if (Flags.isAnnotation(modifiers)) { |
| id = ITreeNode.ANNOTATION; |
| } else if (Flags.isInterface(modifiers)) { |
| id = ITreeNode.INTERFACE; |
| } else { |
| id = ITreeNode.CLASS; |
| } |
| } |
| } catch (CoreException e) { |
| // ignore |
| } |
| } |
| node3 = new TreeNode(id, actualTypeName, null); |
| node2.add(node3); |
| } |
| node3.add(new TreeNode(0, delta.getMessage(), delta)); |
| } |
| } |
| } |
| |
| private int retrieveTypeModifiers(IDelta delta, String typeName, String componentVersionId) throws CoreException { |
| int indexOfOpen = componentVersionId.lastIndexOf('('); |
| String componentID = componentVersionId.substring(0, indexOfOpen); |
| IApiBaseline baseline = ApiBaselineManager.getManager().getApiBaseline(baselineName); |
| int modifiers = 0; |
| if (baseline != null) { |
| IApiComponent apiComponent = baseline.getApiComponent(componentID); |
| int kind = delta.getKind(); |
| if (apiComponent != null && (kind == IDelta.REMOVED)) { |
| // need to handle reexported types |
| IApiTypeRoot typeRoot = null; |
| String id = apiComponent.getSymbolicName(); |
| switch (delta.getFlags()) { |
| case IDelta.REEXPORTED_TYPE: |
| case IDelta.REEXPORTED_API_TYPE: |
| // handle re-exported types |
| // check if the type is provided by a required |
| // component (it could have been |
| // moved/re-exported) |
| String packageName = Util.EMPTY_STRING; |
| int indexOf = typeName.lastIndexOf('.'); |
| if (indexOf != -1) { |
| packageName = typeName.substring(0, indexOf); |
| } |
| IApiComponent[] providers = apiComponent.getBaseline().resolvePackage(apiComponent, packageName); |
| int index = 0; |
| while (typeRoot == null && index < providers.length) { |
| IApiComponent p = providers[index]; |
| if (!p.equals(apiComponent)) { |
| String id2 = p.getSymbolicName(); |
| if (Util.ORG_ECLIPSE_SWT.equals(id2)) { |
| typeRoot = p.findTypeRoot(typeName); |
| } else { |
| typeRoot = p.findTypeRoot(typeName, id2); |
| } |
| } |
| index++; |
| } |
| break; |
| default: |
| if (Util.ORG_ECLIPSE_SWT.equals(id)) { |
| typeRoot = apiComponent.findTypeRoot(typeName); |
| } else { |
| typeRoot = apiComponent.findTypeRoot(typeName, id); |
| } |
| } |
| if (typeRoot != null) { |
| IApiType structure = typeRoot.getStructure(); |
| if (structure != null) { |
| modifiers = structure.getModifiers(); |
| } |
| } |
| } |
| } |
| if (modifiers == 0) { |
| // try the workspace baseline |
| baseline = ApiBaselineManager.getManager().getWorkspaceBaseline(); |
| if (baseline != null) { |
| IApiComponent apiComponent = baseline.getApiComponent(componentID); |
| if (apiComponent != null) { |
| IApiTypeRoot typeRoot = null; |
| String id = apiComponent.getSymbolicName(); |
| switch (delta.getFlags()) { |
| case IDelta.REEXPORTED_TYPE: |
| case IDelta.REEXPORTED_API_TYPE: |
| // handle re-exported types |
| // check if the type is provided by a |
| // required component (it could have been |
| // moved/re-exported) |
| String packageName = Util.EMPTY_STRING; |
| int indexOf = typeName.lastIndexOf('.'); |
| if (indexOf != -1) { |
| packageName = typeName.substring(0, indexOf); |
| } |
| IApiComponent[] providers = apiComponent.getBaseline().resolvePackage(apiComponent, packageName); |
| int index = 0; |
| while (typeRoot == null && index < providers.length) { |
| IApiComponent p = providers[index]; |
| if (!p.equals(apiComponent)) { |
| String id2 = p.getSymbolicName(); |
| if (Util.ORG_ECLIPSE_SWT.equals(id2)) { |
| typeRoot = p.findTypeRoot(typeName); |
| } else { |
| typeRoot = p.findTypeRoot(typeName, id2); |
| } |
| } |
| index++; |
| } |
| break; |
| default: |
| if (Util.ORG_ECLIPSE_SWT.equals(id)) { |
| typeRoot = apiComponent.findTypeRoot(typeName); |
| } else { |
| typeRoot = apiComponent.findTypeRoot(typeName, id); |
| } |
| } |
| if (typeRoot != null) { |
| IApiType structure = typeRoot.getStructure(); |
| modifiers = structure.getModifiers(); |
| } |
| } |
| } |
| } |
| return modifiers; |
| } |
| } |
| if (this.delta == ApiComparator.NO_DELTA) { |
| root.add(new TreeNode(0, ActionMessages.CompareTaskNoChanges, null)); |
| } else { |
| this.delta.accept(new TreeBuilder(root)); |
| } |
| return model; |
| } |
| |
| @Override |
| public String getDescription() { |
| return NLS.bind(ActionMessages.SessionDescription, new String[] { |
| this.timestamp, this.description }); |
| } |
| } |