001: /*
002: * Created on Oct 13, 2004
003: */
004: package uk.org.ponder.rsf.util;
005:
006: import uk.org.ponder.rsf.components.ComponentList;
007: import uk.org.ponder.rsf.components.ELReference;
008: import uk.org.ponder.rsf.components.ParameterList;
009: import uk.org.ponder.rsf.components.UIBound;
010: import uk.org.ponder.rsf.components.UIBranchContainer;
011: import uk.org.ponder.rsf.components.UIComponent;
012: import uk.org.ponder.rsf.components.UIContainer;
013: import uk.org.ponder.rsf.components.UIELBinding;
014: import uk.org.ponder.rsf.components.UIForm;
015: import uk.org.ponder.rsf.components.UIParameter;
016: import uk.org.ponder.rsf.components.UIParameterHolder;
017: import uk.org.ponder.rsf.view.ViewRoot;
018: import uk.org.ponder.util.AssertionException;
019:
020: /**
021: * Low-level utilities for working on RSF component trees.
022: * @author Antranig Basman (antranig@caret.cam.ac.uk)
023: *
024: */
025: public class RSFUtil {
026: /**
027: * This method returns an enclosing Form instance, where one is present in the
028: * tree. Note that this only makes sense for HTML-style forms, and is a coding
029: * convenience. For alternative form models, client code must keep records of
030: * the UIForm instance themselves.
031: */
032: public static UIForm findBasicForm(UIContainer tocheck) {
033: while (tocheck != null) {
034: if (tocheck instanceof UIForm)
035: return (UIForm) tocheck;
036: tocheck = tocheck.parent;
037: }
038: return null;
039: }
040:
041: /**
042: * This method returns an enclosing ViewRoot instance, where one is present in
043: * the tree (it should be, in every well-formed tree)
044: */
045: public static ViewRoot findViewRoot(UIComponent tofind) {
046: while (tofind != null) {
047: if (tofind instanceof ViewRoot)
048: return (ViewRoot) tofind;
049: tofind = tofind.parent;
050: }
051: return null;
052: }
053:
054: /**
055: * A convenience method that assumes the BasicFormModel (uses findBasicForm
056: * above). Adds the supplied name/value pair to the nearest enclosing form
057: * control.
058: */
059:
060: public static void addBasicFormParameter(UIContainer local,
061: UIParameter toadd) {
062: UIForm enclosing = findBasicForm(local);
063: if (enclosing == null) {
064: throw new AssertionException("Component "
065: + local.getFullID() + " has no form parent!");
066: }
067: enclosing.parameters.add(toadd);
068: }
069:
070: /** Adds a "deferred resulting view" EL binding to the supplied UIComponent.
071: * This will transfer a value from an EL path in the request context to a
072: * particular path withing the outgoing ViewParameters state for the coming
073: * action cycle, assuming that it completes without error.
074: *
075: */
076: public static void addResultingViewBinding(
077: UIParameterHolder holder, String viewParamsPath,
078: String requestPath) {
079: if (holder.parameters == null) {
080: holder.parameters = new ParameterList();
081: }
082: holder.parameters.add(new UIELBinding(
083: "ARIResult.resultingView." + viewParamsPath,
084: new ELReference(requestPath)));
085: }
086:
087: /**
088: * Adds a binding to the supplied parameter list that will assign the EL
089: * expression <code>source</code> to <code>transit</code> and then
090: * <code>transit</code> to <code>dest</code>, a classic usage of
091: * "whole-object validation through transit".
092: */
093: public static void addTransitBinding(ParameterList paramlist,
094: String source, String transit, String dest) {
095: paramlist
096: .add(new UIELBinding(transit, new ELReference(source)));
097: paramlist.add(new UIELBinding(dest, new ELReference(transit)));
098: }
099:
100: /**
101: * Determines whether the supplied component has a bound value, and hence will
102: * be visible to fossilized bindings/RSVC processing. It must not only be an
103: * instance of {@link UIBound}, but also have a non-null binding.
104: */
105: public static boolean isBound(UIComponent tocheck) {
106: return tocheck instanceof UIBound ? ((UIBound) tocheck).valuebinding != null
107: : false;
108: }
109:
110: // PROFILER hotspot: 1.5% of request render time.
111: public static String getFullIDSegment(String ID, String localID) {
112: return SplitID.getPrefix(ID) + SplitID.SEPARATOR + localID
113: + SplitID.SEPARATOR;
114: }
115:
116: /**
117: * See {@link UIComponent} for the operation of this algorithm.
118: */
119: // PROFILER hotspot: 2.4% render request time
120: public static String computeFullID(UIComponent tocompute) {
121: StringBuffer togo = new StringBuffer();
122: UIContainer move = null;
123: if (!(tocompute instanceof UIBranchContainer)) {
124: // the tail part of an ID is always the component's leaf ID itself.
125: togo.insert(0, tocompute.ID);
126: move = tocompute.parent;
127: } else {
128: move = (UIContainer) tocompute;
129: }
130: while (move.parent != null) { // ignore the top-level viewroot Branch
131: if (!move.noID) {
132: togo.insert(0, getFullIDSegment(move.ID, move.localID));
133: }
134: move = move.parent;
135: }
136: return togo.toString();
137: }
138:
139: public static String reportPath(UIComponent branch) {
140: String path = branch.getFullID();
141: return path.equals("") ? "component tree root" : "full path "
142: + path;
143: }
144:
145: /** Returns the common ancestor path of s1 and s2 * */
146: public static int commonPath(String s1, String s2) {
147: int s1c = s1.lastIndexOf(':');
148: int s2c = s2.lastIndexOf(':');
149: int i = 0;
150: while (true) {
151: if (i > s1c || i > s2c)
152: break;
153: if (s1.charAt(i) != s2.charAt(i))
154: break;
155: ++i;
156: }
157: return i;
158: }
159:
160: public static ComponentList getRootPath(UIComponent leaf) {
161: ComponentList togo = new ComponentList();
162: while (leaf != null) {
163: togo.add(0, leaf);
164: leaf = leaf.parent;
165: }
166: return togo;
167: }
168:
169: public static void failRemove(UIComponent failed) {
170: throw new IllegalArgumentException("Tried to remove "
171: + failed.getFullID()
172: + " which is not a child of this container");
173: }
174:
175: }
|