package com.framsticks.core; import com.framsticks.params.AccessInterface; import com.framsticks.params.CompositeParam; import com.framsticks.params.Param; import com.framsticks.util.FramsticksException; import com.framsticks.util.dispatching.Dispatching; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.annotation.concurrent.Immutable; import org.apache.commons.collections.ListUtils; /** * @author Piotr Sniegowski */ @Immutable public final class Path { // private final static Logger log = Logger.getLogger(Path.class.getName()); final Instance instance; final String textual; final LinkedList nodes; protected static Object getKnownChild(Instance instance, AccessInterface access, CompositeParam param) { Object child = access.get(param, Object.class); if (child == null) { return null; } try { instance.registry.prepareAccess(param); return child; } catch (FramsticksException e) { } return null; } /** * @param instance * @param textual * @param nodes */ Path(Instance instance, String textual, LinkedList nodes) { this.instance = instance; this.textual = textual; this.nodes = nodes; } public Path appendNode(Node node) { assert isResolved(); return new PathBuilder().instance(instance).textual(textual + ((size() == 1) ? "" : "/") + node.getParam().getId()).add(nodes).add(node).finish(); } public Path appendParam(CompositeParam param) { assert isResolved(); return appendNode(new Node(param, null)); } public static class PathBuilder { Instance instance; String textual; final LinkedList nodes = new LinkedList(); public Path finish() { assert instance != null; assert textual != null; return new Path(instance, textual, nodes); } public PathBuilder() { } public PathBuilder instance(Instance instance) { this.instance = instance; return this; } public PathBuilder textual(String textual) { this.textual = textual; return this; } public PathBuilder add(List nodes) { this.nodes.addAll(nodes); return this; } public PathBuilder add(Node node) { this.nodes.add(node); return this; } public PathBuilder setLast(Object object) { Node n = nodes.pollLast(); nodes.add(new Node(n.getParam(), object)); return this; } public PathBuilder buildUpTo(List nodes, Node node) { StringBuilder b = new StringBuilder(); boolean add = false; for (Node n : nodes) { this.nodes.add(n); if (add) { b.append("/").append(n.getParam().getId()); } add = true; if (n == node) { break; } } this.textual = (nodes.size() == 1) ? "/" : b.toString(); return this; } public PathBuilder resolve(Instance instance, String textual) { assert nodes.isEmpty(); assert instance.isActive(); this.instance = instance; nodes.add(instance.getRoot()); Node current = instance.getRoot(); StringBuilder b = new StringBuilder(); Iterator i = Instance.splitPath(textual); while (i.hasNext() && current.getObject() != null) { AccessInterface access = instance.registry.prepareAccess(current.getParam()); if (access == null) { break; } String e = i.next(); Param p = access.getParam(e); if (!(p instanceof CompositeParam)) { //entries.add(new Entry()); break; } CompositeParam c = (CompositeParam)p; b.append("/").append(e); access.select(current.getObject()); current = new Node(c, getKnownChild(instance, access, c)); nodes.add(current); } this.textual = (nodes.size() == 1) ? "/" : b.toString(); return this; } } public static PathBuilder build() { return new PathBuilder(); } public Path appendResolution(Object object) { assert !isResolved(); Path result = new PathBuilder().textual(textual).instance(instance).add(nodes).setLast(object).finish(); assert size() == result.size(); return result; } public final Object getTopObject() { return getTop().getObject(); } public final Node getTop() { return nodes.getLast(); } public final Node getUnder() { assert nodes.size() >= 2; return nodes.get(nodes.size() - 2); } public final String getTextual() { return textual; } public String toString() { return instance + textual + (!isResolved() ? "!" : ""); } public final int size() { assert Dispatching.isThreadSafe(); return nodes.size(); } public final boolean isResolved() { assert Dispatching.isThreadSafe(); return getTop().getObject() != null; } public final boolean isResolved(String textual) { assert Dispatching.isThreadSafe(); return isTheSame(textual) && isResolved(); } public final boolean isTheSame(String textual) { assert Dispatching.isThreadSafe(); return this.textual.equals(textual); } public final Instance getInstance() { assert Dispatching.isThreadSafe(); return instance; } /** Attach resolution at end, if available. * * @return Modified path, if resolution was available, this otherwise. */ public Path tryFindResolution() { assert instance.isActive(); assert !isResolved(); if (size() == 1) { return Path.build().resolve(instance, "/").finish();//appendResolution(instance.root.object); } Object child = getKnownChild(instance, instance.bindAccess(getUnder()), getTop().getParam()); if (child == null) { return this; } return appendResolution(child); } public boolean matches(Path p) { assert Dispatching.isThreadSafe(); assert instance == p.instance; Iterator a = nodes.iterator(); Iterator b = p.nodes.iterator(); while (a.hasNext() && b.hasNext()) { Node an = a.next(); Node bn = b.next(); if (an.object != bn.object) { return false; } } return a.hasNext() == b.hasNext(); } public String getLastElement() { return getTop().getParam().getId(); } public final boolean isOwner(Instance instance) { return this.instance == instance; } // public void setInstance(Instance instance) { // this.instance = instance; // } @SuppressWarnings("unchecked") public List getNodes() { return ListUtils.unmodifiableList(nodes); } public void assureResolved() { if (!isResolved()) { throw new FramsticksException().msg("path is not resolved").arg("path", this); } } }