/** * */ package com.sap.furcas.runtime.parser.impl.context; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import com.sap.furcas.runtime.common.exceptions.ModelAdapterException; import com.sap.furcas.runtime.common.interfaces.IModelElementInvestigator; import com.sap.furcas.runtime.common.interfaces.IModelElementProxy; /** * keeps track of modelElements being contexts for each other in a text file with regards to a context path. * manages the context as a set of trees (set of roots with children) and is aware of context elements being * proxies at first, and later modelElements. * The interface allows passing anything of Type Object, but the objects passed should really only be Objects * which a ModelAdapter can deal with when passed in findCandidatesInContext(). * Allows searching for elements in context, and caches queries to that method call. */ public class ContextManager { /** static maintenance of element sematic context mapping. */ private Map<Object, Context> contextByElement; private ContextLookUpCache lookupCache; private Set<Context> rootContexts; public ContextManager() { resetContextManager(); } /** * */ private void resetContextManager() { // maybe make public later contextByElement = new HashMap<Object, Context>(); rootContexts = new HashSet<Context>(); lookupCache = new ContextLookUpCache(); } /** * notifies the manager that a proxy has been resolved with another object, * so the manager should from now on treat the new object like the proxy before. * The manager relies on client code to pass a creation context if any. * @param proxy * @param realElement * @param creationContextElement the context in which the proxy had been created, if any (may be null) */ public void notifyProxyResolvedWith(IModelElementProxy proxy, Object realElement, IModelElementProxy creationContextElement) { // proxy element is either a context element itself, or contained in some creationContext Context elementContext = contextByElement.get(proxy); if (elementContext != null) { // change in context contextByElement.remove(proxy); // TODO remove the proxy from context map contextByElement.put(realElement, elementContext); if (elementContext.getElement() == proxy) { elementContext.setElement(realElement); Context parent = elementContext.getParent(); if (parent != null) { parent.replaceElement(proxy, realElement); } } else { elementContext.replaceElement(proxy, realElement); } } if (creationContextElement != null) { Context creationContext = contextByElement.get(creationContextElement); if (creationContext == null && creationContextElement.getRealObject() != null) { creationContext = contextByElement.get(creationContextElement.getRealObject()); } if (creationContext != null) { creationContext.replaceElement(proxy, realElement); } } } /** * @param proxy * @param modelElementProxy */ public void discardProxy(Object proxy, IModelElementProxy creationContextElement) { if (proxy != null) { Context elementContext = contextByElement.get(proxy); if (elementContext != null) { Context parent = elementContext.getParent(); if (parent == null) { // root context is invalid rootContexts.remove(elementContext); } Set<Object> deleted = Context.removeWithChildren(elementContext); // remove all elements without context from map. for (Iterator<Object> iterator = deleted.iterator(); iterator.hasNext();) { Object toDelete = iterator.next(); contextByElement.remove(toDelete); } } Context creationContext = contextByElement.get(creationContextElement); if (creationContext != null) { creationContext.remove(proxy); } } } /** * @param object * @return */ public Object getContextForElement(Object object) { Context objectContext = contextByElement.get(object); if(objectContext == null && object instanceof IModelElementProxy) { objectContext = contextByElement.get(((IModelElementProxy)object).getRealObject()); } if (objectContext != null) { return objectContext.getElement(); } return null; } /** * @param object * @return */ public List<Object> getElementsInContext(Object contextElement) { Context objectContext = contextByElement.get(contextElement); if (objectContext != null) { return objectContext.getElements(); } return null; } /** * Extends the importing context by the elements of the imported context. * @param modelElement * @param realValue */ public void setContextImport(Object importingContextElement, Object importedContextElement) { Context importedContext = contextByElement.get(importedContextElement); Context importingContext = contextByElement.get(importingContextElement); if (importingContext != null && importedContext != null) { importingContext.importContext(importedContext); } } /** * @param modelAdapter * @param context * @param valueTypeName * @param keyValue * @param keyName * @return * @throws AmbiguousLookupException * @throws ModelAdapterException */ public Object findCandidatesInContext(IModelElementInvestigator modelAdapter, Object contextElement, List<String> valueTypeName, String keyName, Object keyValue) throws ModelAdapterException, AmbiguousLookupException { Context context = this.contextByElement.get(contextElement); if (context != null) { // use cache as operations involving modelAdapter may take long. return lookupCache.findCandidatesInContext(modelAdapter, context, valueTypeName, keyValue, keyName); } else { return null; } } /** * @param contextElement * @param ro */ public void addToContext(Object contextElement, Object newElement) { // TODO problem: if contextElement is a proxy but a substitution has already happened in the contextManager, the element won't be found Context context = this.contextByElement.get(contextElement); if (context == null && contextElement instanceof IModelElementProxy && ((IModelElementProxy) contextElement).getRealObject() != null) { // The proxy representing the context element may already have been substituted in // the contextByElement ksy side but still remained as a proxy in the currentContextStack. // If the contextElement passed is a resolved proxy, try to use the real object instead: context = this.contextByElement.get(((IModelElementProxy) contextElement).getRealObject()); } if (context != null) { context.add(newElement); } } /** * returns this context or the first ancestor context (bottom up) having the tag, or null if none. * @param contextElement an element that is a context element (else null is returned) * @param optionalTag null, or a tag that a context need to have. * @return */ public Object getTaggedContext(Object contextElement, String optionalTag) { Object modelElement = null; Context referenceContext = this.contextByElement.get(contextElement); if (referenceContext != null) { if (optionalTag != null) { // search upwards in hierarchy for first context having tag in tags boolean tagFound = false; while (!tagFound && referenceContext != null) { String[] contextTags = referenceContext.getTags(); if (contextTags != null) { for (int i = 0; i < contextTags.length; i++) { String contextTag = contextTags[i]; if (contextTag.equals(optionalTag)) { tagFound = true; break; } } } if (!tagFound) { referenceContext = referenceContext.getParent(); } } } // referenceContext canbecome null by using tags, if none with tag exists if (referenceContext != null) { modelElement = referenceContext.getElement(); } } return modelElement; } /** * @param contextElement * @return */ public Object getContextParent(Object contextElement) { Context referenceContext = this.contextByElement.get(contextElement); if (referenceContext != null) { Context parent = referenceContext.getParent(); if (parent != null) { return parent.getElement(); } } return null; } /** * checks modelElement was part of the document, navigation along * modelElements can lead outside the modelElements represented in the text. * @param navigatedContext * @return true if the element has been parsed from the document, and therefore has a context. */ public boolean hasInTextContext(Object navigatedContext) { return this.contextByElement.get(navigatedContext) != null; } /** * adds a context for the given element within a parent context, creates a content hierarchy. * @param tags * @param currentContextElement * @param element */ public void addContextChildFor(IModelElementProxy parentContextElement, IModelElementProxy newChildElement, String[] tags) { Context context = contextByElement.get(parentContextElement); if (context == null && parentContextElement.getRealObject() != null) { context = contextByElement.get(parentContextElement.getRealObject()); } if (context != null) { Context newContext = context.createContext(newChildElement); newContext.setTags(tags); contextByElement.put(newChildElement, newContext); } else { // should never happen throw new RuntimeException("BUG: addContext called where parent Object has no known context."); } } /** * adds this element as new root context, meaning it is not contained in any other context. * @param newChildElement * @param tags */ public void addRootContext(IModelElementProxy newChildElement, String[] tags) { Context newRootContext = new Context(); newRootContext.setTags(tags); rootContexts.add(newRootContext); newRootContext.setElement(newChildElement); contextByElement.put(newChildElement, newRootContext); } // probably obsolete methods // /** // * // */ // public void resolveAllProxies() { // for (Iterator<Context> iterator = rootContexts.iterator(); iterator.hasNext();) { // Context rootContext = (Context) iterator.next(); // // recursively go through all context nodes // resolveOrRemoveProxies(rootContext); // } // // } // /** // * resolves or removes in the given subtree recursively // * @param rootContext // */ // private void resolveOrRemoveProxies(Context rootContext) { // if (rootContext.getElement() instanceof IModelElementProxy) { // IModelElementProxy proxy = (IModelElementProxy) rootContext.getElement(); // Object realObject = proxy.getRealObject(); // if (realObject == null) { // // could not create or resolve this modelElement from proxy, context is invalid // throw new RuntimeException("Context content Element had not be resolved"); // } // rootContext.setElement(realObject); // } // ContextIterator iterator = rootContext.iterator(); // List<IModelElementProxy> toBeReplaced = new ArrayList<IModelElementProxy>(); // while (iterator.hasNext()) { // Object object = (Object) iterator.next(); // if (object instanceof IModelElementProxy) { // IModelElementProxy proxy = (IModelElementProxy) object; // Object realObject = proxy.getRealObject(); // if (realObject == null) { // // could not create this modelElement from proxy, context is invalid // throw new RuntimeException("Context content Element could not be resolved"); // } // toBeReplaced.add(proxy); // // } // } // for (Iterator<IModelElementProxy> iterator2 = toBeReplaced.iterator(); iterator2.hasNext();) { // IModelElementProxy proxy = (IModelElementProxy) iterator2.next(); // rootContext.replaceElement(proxy, proxy.getRealObject()); // // } // // Collection<Context> children = rootContext.getChildContexts(); // if (children != null) { // for (Iterator<Context> iterator2 = children.iterator(); iterator2.hasNext();) { // Context childcontext = (Context) iterator2.next(); // resolveOrRemoveProxies(childcontext); // } // } // } }