001: /*
002: * Created on Nov 1, 2005
003: */
004: package uk.org.ponder.rsf.componentprocessor;
005:
006: import uk.org.ponder.beanutil.BeanLocator;
007: import uk.org.ponder.beanutil.BeanModelAlterer;
008: import uk.org.ponder.beanutil.BeanResolver;
009: import uk.org.ponder.mapping.ConverterConverter;
010: import uk.org.ponder.mapping.DataConverterRegistry;
011: import uk.org.ponder.mapping.ShellInfo;
012: import uk.org.ponder.rsf.components.ELReference;
013: import uk.org.ponder.rsf.components.UIBound;
014: import uk.org.ponder.rsf.components.UIComponent;
015: import uk.org.ponder.rsf.components.UIForm;
016: import uk.org.ponder.rsf.components.UIParameter;
017: import uk.org.ponder.rsf.components.UIVerbatim;
018: import uk.org.ponder.rsf.request.EarlyRequestParser;
019: import uk.org.ponder.rsf.request.FossilizedConverter;
020: import uk.org.ponder.rsf.request.RequestSubmittedValueCache;
021: import uk.org.ponder.rsf.request.SubmittedValueEntry;
022: import uk.org.ponder.rsf.state.ErrorStateManager;
023: import uk.org.ponder.rsf.uitype.UITypes;
024: import uk.org.ponder.util.Logger;
025: import uk.org.ponder.util.UniversalRuntimeException;
026:
027: /**
028: * Fetches values from the request bean model that are referenced via EL value
029: * bindings, if such have not already been set. Will also compute the fossilized
030: * binding for this component (not a completely cohesively coupled set of
031: * functions, but we are accumulating quite a lot of little processors).
032: *
033: * @author Antranig Basman (antranig@caret.cam.ac.uk)
034: */
035:
036: public class ValueFixer implements ComponentProcessor {
037: private BeanLocator beanlocator;
038: private BeanModelAlterer alterer;
039: private RequestSubmittedValueCache rsvc;
040: // private ErrorStateManager errorStateManager;
041: private boolean renderfossilized;
042: private DataConverterRegistry dataConverterRegistry;
043:
044: public void setBeanLocator(BeanLocator beanlocator) {
045: this .beanlocator = beanlocator;
046: }
047:
048: public void setModelAlterer(BeanModelAlterer alterer) {
049: this .alterer = alterer;
050: }
051:
052: public void setRenderFossilizedForms(boolean renderfossilized) {
053: this .renderfossilized = renderfossilized;
054: }
055:
056: public void setDataConverterRegistry(
057: DataConverterRegistry dataConverterRegistry) {
058: this .dataConverterRegistry = dataConverterRegistry;
059: }
060:
061: private FormModel formModel;
062:
063: public void setFormModel(FormModel formModel) {
064: this .formModel = formModel;
065: }
066:
067: public void setErrorStateManager(ErrorStateManager errorStateManager) {
068: // this.errorStateManager = errorStateManager;
069: if (errorStateManager.errorstate.rsvc != null) {
070: rsvc = errorStateManager.errorstate.rsvc;
071: } else
072: rsvc = new RequestSubmittedValueCache();
073: }
074:
075: // This dependency is here so we can free FC from instance wiring cycle on
076: // RenderSystem. A slight loss of efficiency since this component may never
077: // be rendered - we might think about "lazy processors" at some point...
078: private FossilizedConverter fossilizedconverter;
079:
080: public void setFossilizedConverter(
081: FossilizedConverter fossilizedconverter) {
082: this .fossilizedconverter = fossilizedconverter;
083: }
084:
085: public void processComponent(UIComponent toprocesso) {
086: if (toprocesso instanceof UIBound) {
087: UIBound toprocess = (UIBound) toprocesso;
088: // If there is a value in the SVE, return it to the control.
089: SubmittedValueEntry sve = rsvc.byID(toprocess.getFullID());
090: boolean hadcached = false;
091: Object modelvalue = null;
092: if (sve != null && sve.newvalue != null) {
093: toprocess.updateValue(sve.newvalue);
094: hadcached = true;
095: }
096: UIForm form = formModel.formForComponent(toprocess);
097: boolean getform = form == null ? false : form.type
098: .equals(EarlyRequestParser.RENDER_REQUEST);
099: Object root = getform ? (Object) form.viewparams
100: : beanlocator;
101: if (toprocess.valuebinding != null
102: && (toprocess.acquireValue() == null
103: || UITypes.isPlaceholder(toprocess
104: .acquireValue()) || hadcached)) {
105: // a bound component ALWAYS contains a value of the correct type.
106: Object oldvalue = toprocess.acquireValue();
107: String stripbinding = toprocess.valuebinding.value;
108: BeanResolver resolver = computeResolver(toprocess, root);
109: Object flatvalue = null;
110: try {
111: flatvalue = alterer.getFlattenedValue(stripbinding,
112: root, oldvalue.getClass(), resolver);
113: } catch (Exception e) {
114: // don't let a bad bean model prevent the correct reference being
115: // encoded
116: Logger.log.info("Error resolving EL reference "
117: + stripbinding
118: + " for component with full ID "
119: + toprocess.getFullID(), e);
120: }
121: if (flatvalue != null) {
122: modelvalue = flatvalue;
123: if (!hadcached) {
124: toprocess.updateValue(flatvalue);
125: }
126: }
127: } else if (toprocess.resolver != null) {
128: Object oldvalue = toprocess.acquireValue();
129: // Unclear about this branch - we apply resolvers in the tree to values
130: // which we already find there? Surely the value must already be adequately
131: // flat!
132: BeanResolver resolver = computeResolver(toprocess, root);
133: Object flatvalue = alterer.getFlattenedValue(null,
134: oldvalue, oldvalue.getClass(), resolver);
135: if (flatvalue != null) {
136: toprocess.updateValue(flatvalue);
137: }
138: }
139: if (toprocess.submittingname == null) {
140: toprocess.submittingname = toprocess.getFullID();
141: }
142: if (toprocess.valuebinding != null && !getform) {
143: // TODO: Think carefully whether we want these "encoded" bindings to
144: // EVER appear in the component tree. Tradeoffs - we would need to
145: // create
146: // more classes that renderer could recognise to compute bindings, and
147: // increase its knowledge about the rest of RSF.
148: if (toprocess.fossilize
149: && toprocess.fossilizedbinding == null
150: && renderfossilized) {
151: UIParameter fossilized = fossilizedconverter
152: .computeFossilizedBinding(toprocess,
153: modelvalue);
154: toprocess.fossilizedbinding = fossilized;
155: }
156: if (toprocess.darreshaper != null) {
157: toprocess.fossilizedshaper = fossilizedconverter
158: .computeReshaperBinding(toprocess);
159: }
160: }
161: if (toprocess.acquireValue() == null) {
162: throw new IllegalArgumentException(
163: "Error following value fixup: null bound value found in component "
164: + toprocess + " with full ID "
165: + toprocess.getFullID());
166: }
167: } else if (toprocesso instanceof UIVerbatim) {
168: UIVerbatim toprocess = (UIVerbatim) toprocesso;
169: if (toprocess.markup instanceof ELReference) {
170: ELReference ref = (ELReference) toprocess.markup;
171: toprocess.markup = alterer.getBeanValue(ref.value,
172: beanlocator, null);
173: }
174: }
175: }
176:
177: /**
178: * As well as resolving any reference to a BeanResolver in the
179: * <code>resolver</code> field, this method will also copy it across to the
180: * <code>darreshaper</code> field if a) it refers to a LeafObjectParser, and
181: * b) the field is currently empty. This is a courtesy to allow compactly
182: * encoded things like DateParsers to be used first class, although we REALLY
183: * expect users to make transit beans.
184: */
185: private BeanResolver computeResolver(UIBound toprocess, Object root) {
186: Object renderer = toprocess.resolver;
187:
188: if (renderer == null) {
189: if (toprocess.valuebinding != null) {
190: ShellInfo shells = alterer.fetchShells(
191: toprocess.valuebinding.value, root, false);
192:
193: renderer = dataConverterRegistry.fetchConverter(shells);
194: }
195: }
196:
197: if (renderer instanceof ELReference) {
198: renderer = alterer.getBeanValue(
199: ((ELReference) renderer).value, beanlocator, null);
200: if (toprocess.darreshaper == null) {
201: toprocess.darreshaper = (ELReference) toprocess.resolver;
202: }
203: }
204: if (renderer == null)
205: return null;
206: BeanResolver resolver = ConverterConverter.toResolver(renderer);
207: if (resolver == null) {
208: throw UniversalRuntimeException
209: .accumulate(
210: new IllegalArgumentException(),
211: "Renderer object for "
212: + toprocess.getFullID()
213: + " of unrecognised "
214: + renderer.getClass()
215: + " (expected BeanResolver or LeafObjectParser)");
216: }
217: return resolver;
218: }
219:
220: }
|