package bluej.stride.framedjava.elements;
import bluej.stride.framedjava.ast.AccessPermissionFragment;
import bluej.stride.framedjava.ast.ExpressionSlotFragment;
import bluej.stride.framedjava.ast.FrameFragment;
import bluej.stride.framedjava.ast.JavaFragment;
import bluej.stride.framedjava.ast.StructuredSlotFragment;
import bluej.stride.framedjava.ast.SuperThisFragment;
import bluej.stride.framedjava.ast.TextSlotFragment;
import bluej.stride.framedjava.slots.StructuredSlot;
import nu.xom.Attribute;
import nu.xom.Element;
import nu.xom.Elements;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
| An extension of the XML Element class which also keeps track of enough information
| to later form XPaths to particular Attributes and Elements.
|
public class LocatableElement
extends Element{
| The CodeElement which generated this Element. Will be used for mapping FrameFragments.
|
private final CodeElement origin;
| A map from attribute names (directly contained in this Element) to
| JavaFragments from which those attributes came, if applicable.
|
private final HashMap<String, JavaFragment> attrNames = new HashMap<>();
public LocatableElement(CodeElement origin, String name)
{
super(name);
this.origin = origin;
}
public void addAttributeStructured(String name, StructuredSlotFragment code)
{
attrNames.put(name, code);
addAttribute(new Attribute(name, code.getContent()));
addAttribute(new Attribute(name + "-java", code.getJavaCode()));
}
public void addAttributeAccess(String name, AccessPermissionFragment access)
{
attrNames.put(name, access);
addAttribute(new Attribute(name, access.getContent()));
}
public void addAttributeSuperThis(String name, SuperThisFragment superThis)
{
attrNames.put(name, superThis);
addAttribute(new Attribute(name, superThis.getValue().toString()));
}
public void addAttributeCode(String name, TextSlotFragment content)
{
attrNames.put(name, content);
addAttribute(new Attribute(name, content.getContent()));
}
public static interface LocationMap
{
public String locationFor(JavaFragment fragment);
public String locationFor(CodeElement element);
}
| Builds a location map, from JavaFragment to the XPath within the Element, taking this
| node as the root, and handling all children. So if you have something like:
|
| <class name="Foo" ...>
|...
<methods>
<method access="public" type="void" name="act" enable="true">
...
<body>
<call expression="..." enable="true"/>
<blank/>
<call expression="bar()" expression-java="bar()" enable="true"/>
</body>
</method>
</methods>
</class>
*
* Then the map will map the JavaFragment corresponding to the "bar()" expression, to the
* XPath: "/class[1]/methods[1]/method[1]/body[1]/call[2]/@expression" when called on the class,
* or "/method[1]/body[1]/call[2]/@expression" when called on the Element from the method.
*
* @param siblingCounts The counts of each element type seen so far as children of this
* Element's parent. Needed in building XPaths to make sure we give
| a unique index to each element.
| @return A map from JavaFragment to XPath String identifying the location of that fragment.
|
public LocationMap buildLocationMap()
{
IdentityHashMap<JavaFragment, String> map = new IdentityHashMap<>();
IdentityHashMap<CodeElement, String> elementMap = new IdentityHashMap<>();
addToLocationMap(new HashMap<>(), map::put, elementMap::put);
return new LocationMap()
{
@Override
public String locationFor(JavaFragment fragment)
{
if (map.containsKey(fragment))
{
return map.get(fragment);
}
p.public else if(fragment instanceof FrameFragment)
{
FrameFragment ff = (FrameFragment)fragment;
return elementMap.get(ff.getElement());
}
else{ return null;
}
}
@Override
public String locationFor(CodeElement element)
{
return elementMap.get(element);
}
};
}
private void addToLocationMap(Map<String, Integer> siblingCounts, BiConsumer<JavaFragment, String> map, BiConsumer<CodeElement, String> elementMap)
{
String me = "/" + getLocalName() + "[" + siblingCounts.getOrDefault(getLocalName(), 1) + "]";
siblingCounts.put(getLocalName(), siblingCounts.getOrDefault(getLocalName(), 1) + 1);
if (origin != null)
elementMap.accept(origin, me);
attrNames.forEach((attrName, fragment) -> map.accept(fragment, me + "/@" + attrName));
processChildren(map, elementMap, getChildElements(), me);
}
private static void processChildren(BiConsumer<JavaFragment, String> map, BiConsumer<CodeElement, String> elementMap, Elements childElements, String me)
{
Map<String, Integer> childCounts = new HashMap<>();
for (int i = 0; i < childElements.size(); i++)
{
Element child = childElements.get(i);
if (child instanceof LocatableElement)
{
((LocatableElement)child).addToLocationMap(childCounts,
(frag, path) -> map.accept(frag, me + path),
(el, path) -> elementMap.accept(el, me + path));
}
else
{
String them = "/" + child.getLocalName() + "[" + childCounts.getOrDefault(child.getLocalName(), 1) + "]";
childCounts.put(child.getLocalName(), childCounts.getOrDefault(child.getLocalName(), 1) + 1);
processChildren(map, elementMap, child.getChildElements(), me + them);
}
}
}
}
top,
use,
map,
class LocatableElement
. LocatableElement
. addAttributeStructured
. addAttributeAccess
. addAttributeSuperThis
. addAttributeCode
top,
use,
map,
interface LocatableElement . LocationMap
. locationFor
. locationFor
. buildLocationMap
. locationFor
. if
. locationFor
. addToLocationMap
. processChildren
162 neLoCode
+ 10 LoComm