All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.org.eclipse.jdt.internal.core.JavaElementDeltaBuilder Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2000, 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
 *     Vladimir Piskarev  - Building large Java element deltas is really slow - https://bugs.eclipse.org/443928
 *******************************************************************************/
package org.aspectj.org.eclipse.jdt.internal.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.aspectj.org.eclipse.jdt.core.IAnnotation;
import org.aspectj.org.eclipse.jdt.core.IJavaElement;
import org.aspectj.org.eclipse.jdt.core.IJavaElementDelta;
import org.aspectj.org.eclipse.jdt.core.IParent;
import org.aspectj.org.eclipse.jdt.core.JavaModelException;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.internal.core.util.Util;

/**
 * A java element delta builder creates a java element delta on
 * a java element between the version of the java element
 * at the time the comparator was created and the current version
 * of the java element.
 *
 * It performs this operation by locally caching the contents of
 * the java element when it is created. When the method
 * createDeltas() is called, it creates a delta over the cached
 * contents and the new contents.
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class JavaElementDeltaBuilder {
	/**
	 * The java element handle
	 */
	IJavaElement javaElement;

	/**
	 * The maximum depth in the java element children we should look into
	 */
	int maxDepth = Integer.MAX_VALUE;

	/**
	 * The old handle to info relationships
	 */
	Map infos;
	Map annotationInfos;

	/**
	 * The old position info
	 */
	Map oldPositions;

	/**
	 * The new position info
	 */
	Map newPositions;

	/**
	 * Change delta
	 */
	public JavaElementDelta delta = null;

	/**
	 * List of added elements
	 */
	HashSet added;

	/**
	 * List of removed elements
	 */
	HashSet removed;

	/**
	 * Doubly linked list item
	 */
	static class ListItem {
		public IJavaElement previous;
		public IJavaElement next;

		public ListItem(IJavaElement previous, IJavaElement next) {
			this.previous = previous;
			this.next = next;
		}
	}
/**
 * Creates a java element comparator on a java element
 * looking as deep as necessary.
 */
public JavaElementDeltaBuilder(IJavaElement javaElement) {
	this.javaElement = javaElement;
	initialize();
	recordElementInfo(
		javaElement,
		(JavaModel)this.javaElement.getJavaModel(),
		0);
}
/**
 * Creates a java element comparator on a java element
 * looking only 'maxDepth' levels deep.
 */
public JavaElementDeltaBuilder(IJavaElement javaElement, int maxDepth) {
	this.javaElement = javaElement;
	this.maxDepth = maxDepth;
	initialize();
	recordElementInfo(
		javaElement,
		(JavaModel)this.javaElement.getJavaModel(),
		0);
}
/**
 * Repairs the positioning information
 * after an element has been added
 */
private void added(IJavaElement element) {
	this.added.add(element);
	ListItem current = getNewPosition(element);
	ListItem previous = null, next = null;
	if (current.previous != null)
		previous = getNewPosition(current.previous);
	if (current.next != null)
		next = getNewPosition(current.next);
	if (previous != null)
		previous.next = current.next;
	if (next != null)
		next.previous = current.previous;
}
/**
 * Builds the java element deltas between the old content of the compilation
 * unit and its new content.
 */
public void buildDeltas() {
	this.delta = new JavaElementDelta(this.javaElement);
	// if building a delta on a compilation unit or below,
	// it's a fine grained delta
	if (this.javaElement.getElementType() >= IJavaElement.COMPILATION_UNIT) {
		this.delta.fineGrained();
	}
	recordNewPositions(this.javaElement, 0);
	findAdditions(this.javaElement, 0);
	findDeletions();
	findChangesInPositioning(this.javaElement, 0);
	trimDelta(this.delta);
	if (this.delta.getAffectedChildren().length == 0) {
		// this is a fine grained but not children affected -> mark as content changed
		this.delta.contentChanged();
	}
}
private boolean equals(char[][][] first, char[][][] second) {
	if (first == second)
		return true;
	if (first == null || second == null)
		return false;
	if (first.length != second.length)
		return false;

	for (int i = first.length; --i >= 0;)
		if (!CharOperation.equals(first[i], second[i]))
			return false;
	return true;
}
/**
 * Finds elements which have been added or changed.
 */
private void findAdditions(IJavaElement newElement, int depth) {
	JavaElementInfo oldInfo = getElementInfo(newElement);
	if (oldInfo == null && depth < this.maxDepth) {
		this.delta.added(newElement);
		added(newElement);
	} else {
		removeElementInfo(newElement);
	}

	if (depth >= this.maxDepth) {
		// mark element as changed
		this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
		return;
	}

	JavaElementInfo newInfo = null;
	try {
		newInfo = (JavaElementInfo)((JavaElement)newElement).getElementInfo();
	} catch (JavaModelException npe) {
		return;
	}

	findContentChange(oldInfo, newInfo, newElement);

	if (oldInfo != null && newElement instanceof IParent) {

		IJavaElement[] children = newInfo.getChildren();
		if (children != null) {
			int length = children.length;
			for(int i = 0; i < length; i++) {
				findAdditions(children[i], depth + 1);
			}
		}
	}
}
/**
 * Looks for changed positioning of elements.
 */
private void findChangesInPositioning(IJavaElement element, int depth) {
	if (depth >= this.maxDepth || this.added.contains(element) || this.removed.contains(element))
		return;

	if (!isPositionedCorrectly(element)) {
		this.delta.changed(element, IJavaElementDelta.F_REORDER);
	}

	if (element instanceof IParent) {
		JavaElementInfo info = null;
		try {
			info = (JavaElementInfo)((JavaElement)element).getElementInfo();
		} catch (JavaModelException npe) {
			return;
		}

		IJavaElement[] children = info.getChildren();
		if (children != null) {
			int length = children.length;
			for(int i = 0; i < length; i++) {
				findChangesInPositioning(children[i], depth + 1);
			}
		}
	}
}
private void findAnnotationChanges(IAnnotation[] oldAnnotations, IAnnotation[] newAnnotations, IJavaElement parent) {
	ArrayList annotationDeltas = null;
	for (int i = 0, length = newAnnotations.length; i < length; i++) {
		IAnnotation newAnnotation = newAnnotations[i];
		Object oldInfo = this.annotationInfos.remove(newAnnotation);
		if (oldInfo == null) {
			JavaElementDelta annotationDelta = new JavaElementDelta(newAnnotation);
			annotationDelta.added();
			if (annotationDeltas == null) annotationDeltas = new ArrayList();
			annotationDeltas.add(annotationDelta);
			continue;
		} else {
			AnnotationInfo newInfo = null;
			try {
				newInfo = (AnnotationInfo) ((JavaElement) newAnnotation).getElementInfo();
			} catch (JavaModelException npe) {
				return;
			}
			if (!Util.equalArraysOrNull(((AnnotationInfo) oldInfo).members, newInfo.members)) {
				JavaElementDelta annotationDelta = new JavaElementDelta(newAnnotation);
				annotationDelta.changed(IJavaElementDelta.F_CONTENT);
				if (annotationDeltas == null) annotationDeltas = new ArrayList();
				annotationDeltas.add(annotationDelta);
			}
		}
	}
	for (int i = 0, length = oldAnnotations.length; i < length; i++) {
		IAnnotation oldAnnotation = oldAnnotations[i];
		if (this.annotationInfos.remove(oldAnnotation) != null) {
			JavaElementDelta annotationDelta = new JavaElementDelta(oldAnnotation);
			annotationDelta.removed();
			if (annotationDeltas == null) annotationDeltas = new ArrayList();
			annotationDeltas.add(annotationDelta);		}
	}
	if (annotationDeltas == null)
		return;
	int size = annotationDeltas.size();
	if (size > 0) {
		JavaElementDelta parentDelta = this.delta.changed(parent, IJavaElementDelta.F_ANNOTATIONS);
		parentDelta.annotationDeltas = (IJavaElementDelta[]) annotationDeltas.toArray(new IJavaElementDelta[size]);
	}
}
/**
 * The elements are equivalent, but might have content changes.
 */
private void findContentChange(JavaElementInfo oldInfo, JavaElementInfo newInfo, IJavaElement newElement) {
	if (oldInfo instanceof MemberElementInfo && newInfo instanceof MemberElementInfo) {
		if (((MemberElementInfo)oldInfo).getModifiers() != ((MemberElementInfo)newInfo).getModifiers()) {
			this.delta.changed(newElement, IJavaElementDelta.F_MODIFIERS);
		}
		if (oldInfo instanceof AnnotatableInfo && newInfo instanceof AnnotatableInfo) {
			findAnnotationChanges(((AnnotatableInfo) oldInfo).annotations, ((AnnotatableInfo) newInfo).annotations, newElement);
		}
		if (oldInfo instanceof SourceMethodElementInfo && newInfo instanceof SourceMethodElementInfo) {
			SourceMethodElementInfo oldSourceMethodInfo = (SourceMethodElementInfo)oldInfo;
			SourceMethodElementInfo newSourceMethodInfo = (SourceMethodElementInfo)newInfo;
			if (!CharOperation.equals(oldSourceMethodInfo.getReturnTypeName(), newSourceMethodInfo.getReturnTypeName())
					|| !CharOperation.equals(oldSourceMethodInfo.getTypeParameterNames(), newSourceMethodInfo.getTypeParameterNames())
					|| !equals(oldSourceMethodInfo.getTypeParameterBounds(), newSourceMethodInfo.getTypeParameterBounds())) {
				this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
			}
		} else if (oldInfo instanceof SourceFieldElementInfo && newInfo instanceof SourceFieldElementInfo) {
			if (!CharOperation.equals(
					((SourceFieldElementInfo)oldInfo).getTypeName(),
					((SourceFieldElementInfo)newInfo).getTypeName())) {
				this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
			}
		} else if (oldInfo instanceof SourceTypeElementInfo && newInfo instanceof SourceTypeElementInfo) {
			SourceTypeElementInfo oldSourceTypeInfo = (SourceTypeElementInfo)oldInfo;
			SourceTypeElementInfo newSourceTypeInfo = (SourceTypeElementInfo)newInfo;
			if (!CharOperation.equals(oldSourceTypeInfo.getSuperclassName(), newSourceTypeInfo.getSuperclassName())
					|| !CharOperation.equals(oldSourceTypeInfo.getInterfaceNames(), newSourceTypeInfo.getInterfaceNames())) {
				this.delta.changed(newElement, IJavaElementDelta.F_SUPER_TYPES);
			}
			if (!CharOperation.equals(oldSourceTypeInfo.getTypeParameterNames(), newSourceTypeInfo.getTypeParameterNames())
					|| !equals(oldSourceTypeInfo.getTypeParameterBounds(), newSourceTypeInfo.getTypeParameterBounds())) {
				this.delta.changed(newElement, IJavaElementDelta.F_CONTENT);
			}
			findCategoryChange(oldSourceTypeInfo.categories, newSourceTypeInfo.categories);
		} else if (oldInfo instanceof ModuleDescriptionInfo && newInfo instanceof ModuleDescriptionInfo) {
			ModuleDescriptionInfo oldSourceModuleInfo = (ModuleDescriptionInfo)oldInfo;
			ModuleDescriptionInfo newSourceModuleInfo = (ModuleDescriptionInfo)newInfo;
			findCategoryChange(oldSourceModuleInfo.getCategories(), newSourceModuleInfo.getCategories());
		}
	}
}
private void findCategoryChange(Map oldCategoriesMap, Map newCategoriesMap) {
	if (oldCategoriesMap != null) {
		// take the union of old and new categories elements (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=125675)
		Set elements;
		if (newCategoriesMap != null) {
			elements = new HashSet(oldCategoriesMap.keySet());
			elements.addAll(newCategoriesMap.keySet());
		} else
			elements = oldCategoriesMap.keySet();
		Iterator iterator = elements.iterator();
		while (iterator.hasNext()) {
			IJavaElement element = (IJavaElement) iterator.next();
			String[] oldCategories = oldCategoriesMap.get(element);
			String[] newCategories = newCategoriesMap == null ? null : (String[]) newCategoriesMap.get(element);
			if (!Util.equalArraysOrNull(oldCategories, newCategories)) {
				this.delta.changed(element, IJavaElementDelta.F_CATEGORIES);
			}
		}
	} else if (newCategoriesMap != null) {
		Iterator elements = newCategoriesMap.keySet().iterator();
		while (elements.hasNext()) {
			IJavaElement element = (IJavaElement) elements.next();
			this.delta.changed(element, IJavaElementDelta.F_CATEGORIES); // all categories for this element were removed
		}
	}
}
/**
 * Adds removed deltas for any handles left in the table
 */
private void findDeletions() {
	Iterator iter = this.infos.keySet().iterator();
	while(iter.hasNext()) {
		IJavaElement element = (IJavaElement)iter.next();
		this.delta.removed(element);
		removed(element);
	}
}
private JavaElementInfo getElementInfo(IJavaElement element) {
	return (JavaElementInfo)this.infos.get(element);
}
private ListItem getNewPosition(IJavaElement element) {
	return (ListItem)this.newPositions.get(element);
}
private ListItem getOldPosition(IJavaElement element) {
	return (ListItem)this.oldPositions.get(element);
}
private void initialize() {
	this.infos = new HashMap(20);
	this.oldPositions = new HashMap(20);
	this.newPositions = new HashMap(20);
	this.oldPositions.put(this.javaElement, new ListItem(null, null));
	this.newPositions.put(this.javaElement, new ListItem(null, null));
	this.added = new HashSet(5);
	this.removed = new HashSet(5);
}
/**
 * Inserts position information for the elements into the new or old positions table
 */
private void insertPositions(IJavaElement[] elements, boolean isNew) {
	int length = elements.length;
	IJavaElement previous = null, current = null, next = (length > 0) ? elements[0] : null;
	for(int i = 0; i < length; i++) {
		previous = current;
		current = next;
		next = (i + 1 < length) ? elements[i + 1] : null;
		if (isNew) {
			this.newPositions.put(current, new ListItem(previous, next));
		} else {
			this.oldPositions.put(current, new ListItem(previous, next));
		}
	}
}
/**
 * Returns whether the elements position has not changed.
 */
private boolean isPositionedCorrectly(IJavaElement element) {
	ListItem oldListItem = getOldPosition(element);
	if (oldListItem == null) return false;

	ListItem newListItem = getNewPosition(element);
	if (newListItem == null) return false;

	IJavaElement oldPrevious = oldListItem.previous;
	IJavaElement newPrevious = newListItem.previous;
	if (oldPrevious == null) {
		return newPrevious == null;
	} else {
		return oldPrevious.equals(newPrevious);
	}
}
/**
 * Records this elements info, and attempts
 * to record the info for the children.
 */
private void recordElementInfo(IJavaElement element, JavaModel model, int depth) {
	if (depth >= this.maxDepth) {
		return;
	}
	JavaElementInfo info = (JavaElementInfo)JavaModelManager.getJavaModelManager().getInfo(element);
	if (info == null) // no longer in the java model.
		return;
	this.infos.put(element, info);

	if (element instanceof IParent) {
		IJavaElement[] children = info.getChildren();
		if (children != null) {
			insertPositions(children, false);
			for(int i = 0, length = children.length; i < length; i++)
				recordElementInfo(children[i], model, depth + 1);
		}
	}
	IAnnotation[] annotations = null;
	if (info instanceof AnnotatableInfo)
		annotations = ((AnnotatableInfo) info).annotations;
	if (annotations != null) {
		if (this.annotationInfos == null)
			this.annotationInfos = new HashMap();
		JavaModelManager manager = JavaModelManager.getJavaModelManager();
		for (int i = 0, length = annotations.length; i < length; i++) {
			this.annotationInfos.put(annotations[i], manager.getInfo(annotations[i]));
		}
	}
}
/**
 * Fills the newPositions hashtable with the new position information
 */
private void recordNewPositions(IJavaElement newElement, int depth) {
	if (depth < this.maxDepth && newElement instanceof IParent) {
		JavaElementInfo info = null;
		try {
			info = (JavaElementInfo)((JavaElement)newElement).getElementInfo();
		} catch (JavaModelException npe) {
			return;
		}

		IJavaElement[] children = info.getChildren();
		if (children != null) {
			insertPositions(children, true);
			for(int i = 0, length = children.length; i < length; i++) {
				recordNewPositions(children[i], depth + 1);
			}
		}
	}
}
/**
 * Repairs the positioning information
 * after an element has been removed
 */
private void removed(IJavaElement element) {
	this.removed.add(element);
	ListItem current = getOldPosition(element);
	ListItem previous = null, next = null;
	if (current.previous != null)
		previous = getOldPosition(current.previous);
	if (current.next != null)
		next = getOldPosition(current.next);
	if (previous != null)
		previous.next = current.next;
	if (next != null)
		next.previous = current.previous;

}
private void removeElementInfo(IJavaElement element) {
	this.infos.remove(element);
}
@Override
public String toString() {
	StringBuilder buffer = new StringBuilder();
	buffer.append("Built delta:\n"); //$NON-NLS-1$
	buffer.append(this.delta == null ? "" : this.delta.toString()); //$NON-NLS-1$
	return buffer.toString();
}
/**
 * Trims deletion deltas to only report the highest level of deletion
 */
private void trimDelta(JavaElementDelta elementDelta) {
	if (elementDelta.getKind() == IJavaElementDelta.REMOVED) {
		elementDelta.clearAffectedChildren();
	} else {
		IJavaElementDelta[] children = elementDelta.getAffectedChildren();
		for(int i = 0, length = children.length; i < length; i++) {
			trimDelta((JavaElementDelta)children[i]);
		}
	}
}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy