001: /*
002: * Created on Nov 25, 2005
003: */
004: package uk.org.ponder.rsf.expander;
005:
006: import java.util.Enumeration;
007:
008: import uk.org.ponder.beanutil.BeanLocator;
009: import uk.org.ponder.mapping.DARApplier;
010: import uk.org.ponder.reflect.DeepBeanCloner;
011: import uk.org.ponder.rsf.components.ComponentList;
012: import uk.org.ponder.rsf.components.ELReference;
013: import uk.org.ponder.rsf.components.ParameterList;
014: import uk.org.ponder.rsf.components.UIBound;
015: import uk.org.ponder.rsf.components.UIBranchContainer;
016: import uk.org.ponder.rsf.components.UIComponent;
017: import uk.org.ponder.rsf.components.UIContainer;
018: import uk.org.ponder.rsf.components.UIDeletionBinding;
019: import uk.org.ponder.rsf.components.UIELBinding;
020: import uk.org.ponder.rsf.components.UIParameter;
021: import uk.org.ponder.rsf.components.UIReplicator;
022: import uk.org.ponder.rsf.components.UISwitch;
023: import uk.org.ponder.rsf.uitype.UITypes;
024: import uk.org.ponder.rsf.viewstate.EntityCentredViewParameters;
025: import uk.org.ponder.saxalizer.AccessMethod;
026: import uk.org.ponder.saxalizer.MethodAnalyser;
027: import uk.org.ponder.saxalizer.SAXAccessMethod;
028: import uk.org.ponder.util.EnumerationConverter;
029: import uk.org.ponder.util.UniversalRuntimeException;
030:
031: /**
032: * Expands a "proto-template" file as read off disk, cloning all members into
033: * non-shared state ready for fixups, in addition removing by expansion all
034: * non-component elements such as UIReplicator and UISwitch.
035: *
036: * @author Antranig Basman (antranig@caret.cam.ac.uk)
037: *
038: */
039:
040: public class TemplateExpander {
041: private DARApplier darapplier;
042: private BeanLocator rbl;
043: private DeepBeanCloner deepcloner;
044:
045: public void setDARApplier(DARApplier darapplier) {
046: this .darapplier = darapplier;
047: }
048:
049: // This is a proxied request-scope dependency!!
050: public void setSafeBeanLocator(BeanLocator beanlocator) {
051: this .rbl = beanlocator;
052: }
053:
054: public void setDeepBeanCloner(DeepBeanCloner deepcloner) {
055: this .deepcloner = deepcloner;
056: }
057:
058: private String computeLocalID(Object bean, Object idstrategy,
059: int index) {
060: String localid = null;
061: if (idstrategy instanceof DirectIndexStrategy) {
062: localid = Integer.toString(index);
063: } else {
064: IDRemapStrategy remapstrategy = (IDRemapStrategy) idstrategy;
065: MethodAnalyser ma = darapplier.getMappingContext()
066: .getAnalyser(bean.getClass());
067: AccessMethod sam = ma
068: .getAccessMethod(remapstrategy.idfield);
069: localid = sam.getChildObject(bean).toString();
070: }
071: return localid;
072: }
073:
074: private String computeStump(UIReplicator replicator) {
075: String stump = null;
076: if (replicator.idstrategy instanceof DirectIndexStrategy) {
077: stump = replicator.valuebinding.value;
078: } else {
079: IDRemapStrategy remapstrategy = (IDRemapStrategy) replicator.idstrategy;
080: stump = remapstrategy.basepath.value;
081: }
082: return stump;
083: }
084:
085: private static class RemapState {
086: public String idwildcard;
087: public String localid;
088: public String stump;
089:
090: public RemapState(String localid, String stump,
091: String idwildcard) {
092: this .localid = localid;
093: this .stump = stump;
094: this .idwildcard = idwildcard;
095: }
096: }
097:
098: private static void rewritePossibleELRef(Object elrefo,
099: RemapState state) {
100: if (elrefo instanceof ELReference && state != null) {
101: ELReference elref = (ELReference) elrefo;
102: if (elref.value.startsWith(state.idwildcard)) {
103: elref.value = state.stump
104: + "."
105: + state.localid
106: + elref.value.substring(state.idwildcard
107: .length());
108: }
109: }
110: }
111:
112: private static void rewriteParameterList(ParameterList parameters,
113: RemapState state) {
114: for (int j = 0; j < parameters.size(); ++j) {
115: UIParameter param = parameters.parameterAt(j);
116: if (param instanceof UIELBinding) {
117: UIELBinding elbinding = (UIELBinding) param;
118: rewritePossibleELRef(elbinding.valuebinding, state);
119: rewritePossibleELRef(elbinding.rvalue, state);
120: } else if (param instanceof UIDeletionBinding) {
121: rewritePossibleELRef(
122: ((UIDeletionBinding) param).deletebinding,
123: state);
124: }
125: }
126: }
127:
128: private UIComponent resolveSwitch(UISwitch switchh, RemapState state) {
129: Object lvalue = switchh.lvalue;
130: if (lvalue instanceof ELReference) {
131: lvalue = darapplier.getBeanValue(
132: ((ELReference) lvalue).value, rbl, null);
133: }
134: Object rvalue = switchh.rvalue;
135: if (rvalue instanceof ELReference) {
136: rvalue = darapplier.getBeanValue(
137: ((ELReference) rvalue).value, rbl, null);
138: }
139: UIComponent toadd = lvalue.equals(rvalue) ? switchh.truecomponent
140: : switchh.falsecomponent;
141: return toadd;
142: }
143:
144: /**
145: * @param target A "to be live" container to receiver cloned (expanded)
146: * children correponding to child and any descendents.
147: * @param toclone A "template" component which is to be expanded into children
148: * of the target.
149: * @param state The current remapping state.
150: */
151: private UIComponent cloneComponent(UIComponent toclone,
152: RemapState state) {
153: if (toclone instanceof UISwitch) {
154: UIComponent resolved = resolveSwitch((UISwitch) toclone,
155: state);
156: return cloneComponent(resolved, state);
157: } else {
158: MethodAnalyser ma = darapplier.getMappingContext()
159: .getAnalyser(toclone.getClass());
160: UIComponent cloned = (UIComponent) deepcloner
161: .emptyClone(toclone);
162: for (int i = 0; i < ma.allgetters.length; ++i) {
163: SAXAccessMethod sam = ma.allgetters[i];
164: if (!sam.canGet() || !sam.canSet())
165: continue;
166: if (sam.tagname.equals("parent")) {
167: continue;
168: }
169: if (toclone instanceof UIBound
170: && sam.tagname.equals("value")) {
171: UIBound bound = (UIBound) toclone;
172: UIBound copy = (UIBound) cloned;
173: // use care when copying bound VALUE since placeholder values operate
174: // object handle identity semantics
175: if (UITypes.isPlaceholder(bound.acquireValue())) {
176: copy.updateValue(bound.acquireValue());
177: } else {
178: Object valuecopy = deepcloner.cloneBean(bound
179: .acquireValue());
180: copy.updateValue(valuecopy);
181: }
182: continue;
183: }
184:
185: Object child = sam.getChildObject(toclone);
186: Object clonechild = null;
187:
188: if (child instanceof UIComponent) {
189: UIComponent childcomp = (UIComponent) child;
190: if (child instanceof UIReplicator
191: && !(toclone instanceof UIContainer)) {
192: throw UniversalRuntimeException
193: .accumulate(
194: new IllegalArgumentException(),
195: "UIReplicator "
196: + childcomp.ID
197: + " must have a parent which is a container - in fact "
198: + toclone.getClass());
199: }
200: clonechild = cloneComponent(childcomp, state);
201: } else {
202: // non-component child can be cloned normally
203: clonechild = deepcloner.cloneBean(child);
204: }
205: if (state != null) {
206: if (clonechild instanceof ELReference) {
207: rewritePossibleELRef(clonechild, state);
208: }
209: if (clonechild instanceof ParameterList) {
210: rewriteParameterList(
211: (ParameterList) clonechild, state);
212: } else if (clonechild instanceof EntityCentredViewParameters) {
213: EntityCentredViewParameters ecvp = (EntityCentredViewParameters) clonechild;
214: if (ecvp.entity.ID.equals(state.idwildcard)) {
215: ecvp.entity.ID = state.localid;
216: }
217: }
218: }
219: sam.setChildObject(cloned, clonechild);
220: }
221: // there is a "leaf component tree" here, which may include a UIBound.
222: if (toclone instanceof UIContainer) {
223: cloneChildren((UIContainer) cloned,
224: (UIContainer) toclone, state);
225: }
226: return cloned;
227: }
228: }
229:
230: // Clone all the children of the "source" container into children
231: // of the target.
232: private void cloneChildren(UIContainer target, UIContainer source,
233: RemapState state) {
234: ComponentList children = source.flattenChildren();
235: for (int i = 0; i < children.size(); ++i) {
236: UIComponent child = (UIComponent) children.get(i);
237: if (child instanceof UIReplicator) {
238: expandReplicator(target, (UIReplicator) child, state);
239: } else {
240: UIComponent cloned = cloneComponent(child, state);
241: target.addComponent(cloned);
242: }
243:
244: }
245: }
246:
247: /**
248: * Expands the supplied replicator, encountered as a roadblock in an expanding
249: * prototemplate, into the target branch container in the accreting true
250: * template.
251: *
252: * @param target The branch container which will receive replicated instances
253: * of the replicator's container as replicated children.
254: */
255: private void expandReplicator(UIContainer target,
256: UIReplicator replicator, RemapState state) {
257: // TODO: work out how to remap recursively - currently old remapstate is
258: // thrown away.
259: String listbinding = replicator.valuebinding.value;
260: Object collection = darapplier.getBeanValue(listbinding, rbl,
261: null);
262: int index = 0;
263: // for each member of the object "list", instantiate a BranchContainer with
264: // corresponding localID, and then recurse further.
265: for (Enumeration colit = EnumerationConverter
266: .getEnumeration(collection); colit.hasMoreElements();) {
267: Object bean = colit.nextElement();
268: String localid = computeLocalID(bean,
269: replicator.idstrategy, index);
270: UIContainer expandtarget = target;
271:
272: if (!replicator.elideparent) {
273: UIBranchContainer replicated = UIBranchContainer.make(
274: target, replicator.component.ID, localid);
275: expandtarget = replicated;
276: }
277: String stump = computeStump(replicator);
278: RemapState newstate = new RemapState(localid, stump,
279: replicator.idwildcard);
280:
281: cloneChildren(expandtarget, replicator.component, newstate);
282: // parameters of all other containers are taken care of in cloneComponent.
283: ParameterList clonedparams = (ParameterList) deepcloner
284: .cloneBean(replicator.component.parameters);
285: rewriteParameterList(clonedparams, newstate);
286: expandtarget.parameters = clonedparams;
287: ++index;
288: }
289: }
290:
291: public void expandTemplate(UIContainer target, UIContainer source) {
292: cloneChildren(target, source, null);
293: }
294:
295: }
|