/*******************************************************************************
* Copyright (c) 2006-2014
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* 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:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
package org.emftext.language.java.resource.java.analysis.helper;
import java.util.List;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.emftext.language.java.commons.NamedElement;
import org.emftext.language.java.resource.java.analysis.decider.IResolutionTargetDecider;
/**
* This class can be used to traverse a model tree after parsing for reference
* resolving. It follows scoping rules common to textual languages.
* <p>
* It starts at the point of the reference, walking <i>up</i> the tree but
* visiting, for each step up, the children of the new parent. On each visited
* element it applies a set of deciders with which it is initialized.
*/
public class ScopedTreeWalker {
protected List<IResolutionTargetDecider> deciderList;
private EObject currentBestResult = null;
private boolean finished = false;
/**
* Initializes a new walker with a list of deciders.
*
* @param deciderList
*/
public ScopedTreeWalker(List<IResolutionTargetDecider> deciderList) {
this.deciderList = deciderList;
}
/**
* Main method.
*
* @param startingPoint
* @param identifier
* @param container
* @param crossReference
* @return the target if one was found.
*/
public EObject walk(EObject startingPoint,
String identifier, EObject container,
EReference crossReference) {
if (startingPoint == null) {
return null;
}
//deactivate deciders not suited here at all
for(IResolutionTargetDecider decider : deciderList) {
if (!decider.canFindTargetsFor(container, crossReference)) {
decider.deactivate();
}
}
doWalk(identifier, startingPoint, null, -1);
for(IResolutionTargetDecider decider : deciderList) {
decider.activate();
}
return currentBestResult;
}
private void doWalk(String identifier, EObject startingPoint, EReference navOrigin, int posInNavOrigin) {
searchInDirectChildren(identifier, startingPoint, navOrigin, posInNavOrigin);
if(finished) {
return;
}
searchInAdditionalContent(identifier, startingPoint, navOrigin, posInNavOrigin);
if(finished) {
return;
}
for(IResolutionTargetDecider decider : deciderList) {
if (decider.isActive()) {
walkDown(decider, identifier, startingPoint, navOrigin, posInNavOrigin);
}
}
if(finished) {
return;
}
walkUp(identifier, startingPoint);
}
private void walkUp(String identifier, EObject container) {
//walk up
if (container.eContainer() != null) {
EReference navOrigin = container.eContainmentFeature();
int posInNavOrigin = 0;
if (navOrigin.isMany()) {
EList<?> value = (EList<?>)container.eContainer().eGet(navOrigin);
posInNavOrigin = value.indexOf(container);
}
doWalk(identifier, container.eContainer(), navOrigin, posInNavOrigin);
}
}
private void walkDown(IResolutionTargetDecider decider, String identifier, EObject container, EReference navOrigin, int posInNavOrigin) {
EClass containerClass = container.eClass();
for(EReference reference : getReferences(containerClass)) {
if(reference.isContainment()) {
EList<EObject> contentList = null;
if (decider.continueAfterReference()) {
contentList = getContentList(container, reference, null, -1);
}
else {
contentList = getContentList(container, reference, navOrigin, posInNavOrigin);
}
for(EObject element : contentList) {
if(decider.walkInto(element)) {
searchInDirectChildren(identifier, element, null, -1);
//walk further down
walkDown(decider, identifier, element, null, -1);
}
}
}
}
}
/**
* Looks for last best fit.
*
* @param identifier
* @param container
* @param navOrigin
* @param posInNavOrigin
*/
private void searchInDirectChildren(String identifier, EObject container, EReference navOrigin, int posInNavOrigin) {
EClass containerClass = container.eClass();
for(IResolutionTargetDecider decider : deciderList) {
if (decider.isActive()) {
for(EReference reference : getReferences(containerClass)) {
if(reference.isContainment()) {
if(decider.containsCandidates(container, reference)) {
EList<EObject> contentList = null;
if (decider.continueAfterReference()) {
contentList = getContentList(container, reference, null, -1);
}
else {
contentList = getContentList(container, reference, navOrigin, posInNavOrigin);
}
for(EObject element : contentList) {
if(decider.isPossibleTarget(identifier, element)) {
currentBestResult = element;
}
}
if (currentBestResult != null && decider.isSure()) {
finished = true;
return;
}
}
}
}
}
}
}
private EList<EReference> getReferences(EClass containerClass) {
EList<EReference> references = containerClass.getEAllReferences();
return references;
}
/**
* Looks for first best fit.
*
* @param identifier
* @param container
* @param navOrigin
* @param posInNavOrigin
*/
private void searchInAdditionalContent(String identifier, EObject container, EReference navOrigin, int posInNavOrigin) {
for(IResolutionTargetDecider decider : deciderList) {
if (decider.isActive()) {
EList<? extends EObject> additionalCandidates = decider.getAdditionalCandidates(identifier, container);
if (additionalCandidates != null) {
for(EObject element : additionalCandidates) {
if(decider.isPossibleTarget(identifier, element)) {
currentBestResult = element;
if (decider.isSure()) {
if (!currentBestResult.eIsProxy() && currentBestResult.eResource() == null) {
Resource containerResource = container.eResource();
if (containerResource != null) {
//for package references and the array length field:
//check if there already is a suitable element in
//the resource, or create one if not.
for (EObject content : container.eResource().getContents()) {
if (content.eClass().equals(currentBestResult.eClass())) {
if (content instanceof NamedElement) {
NamedElement cand = (NamedElement) content;
if (((NamedElement) content).getName().equals(((NamedElement)currentBestResult).getName())) {
currentBestResult = cand;
}
}
}
}
if (currentBestResult.eResource() == null) {
containerResource.getContents().add(currentBestResult);
}
}
}
finished = true;
return;
}
}
}
}
}
}
}
private EList<EObject> getContentList(EObject container, EReference reference, EReference navOrigin, int posInNavOrigin) {
EList<EObject> contentList = new BasicEList<EObject>();
if(!reference.isMany()) {
EObject value = (EObject)container.eGet(reference);
contentList.add(value);
}
else {
@SuppressWarnings("unchecked")
EList<EObject> value = (EList<EObject>)container.eGet(reference);
if (!reference.equals(navOrigin)) {
contentList.addAll(value);
}
else {
contentList.addAll(value.subList(0, posInNavOrigin + 1));
}
}
return contentList;
}
}