001: /* ***** BEGIN LICENSE BLOCK *****
002: * Version: MPL 1.1
003: * The contents of this file are subject to the Mozilla Public License Version
004: * 1.1 (the "License"); you may not use this file except in compliance with
005: * the License. You may obtain a copy of the License at
006: * http://www.mozilla.org/MPL/
007: *
008: * Software distributed under the License is distributed on an "AS IS" basis,
009: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
010: * for the specific language governing rights and limitations under the
011: * License.
012: *
013: * The Original Code is Riot.
014: *
015: * The Initial Developer of the Original Code is
016: * Neteye GmbH.
017: * Portions created by the Initial Developer are Copyright (C) 2006
018: * the Initial Developer. All Rights Reserved.
019: *
020: * Contributor(s):
021: * Felix Gnass [fgnass at neteye dot de]
022: *
023: * ***** END LICENSE BLOCK ***** */
024: package org.riotfamily.riot.form.element;
025:
026: import java.io.PrintWriter;
027:
028: import org.riotfamily.common.beans.PropertyUtils;
029: import org.riotfamily.common.beans.ProtectedBeanWrapper;
030: import org.riotfamily.common.i18n.MessageResolver;
031: import org.riotfamily.common.markup.DocumentWriter;
032: import org.riotfamily.common.markup.Html;
033: import org.riotfamily.common.markup.TagWriter;
034: import org.riotfamily.common.util.FormatUtils;
035: import org.riotfamily.forms.Form;
036: import org.riotfamily.forms.element.select.AbstractChooser;
037: import org.riotfamily.riot.dao.RiotDao;
038: import org.riotfamily.riot.editor.EditorDefinition;
039: import org.riotfamily.riot.editor.EditorDefinitionUtils;
040: import org.riotfamily.riot.editor.EditorRepository;
041: import org.riotfamily.riot.editor.ListDefinition;
042: import org.riotfamily.riot.form.ui.FormUtils;
043: import org.riotfamily.riot.list.ColumnConfig;
044: import org.riotfamily.riot.list.ListConfig;
045: import org.riotfamily.riot.list.ui.render.CellRenderer;
046: import org.riotfamily.riot.list.ui.render.RenderContext;
047: import org.springframework.beans.BeansException;
048: import org.springframework.beans.factory.BeanFactory;
049: import org.springframework.beans.factory.BeanFactoryAware;
050: import org.springframework.beans.factory.BeanFactoryUtils;
051: import org.springframework.beans.factory.ListableBeanFactory;
052: import org.springframework.util.Assert;
053: import org.springframework.util.StringUtils;
054:
055: public class ObjectChooser extends AbstractChooser implements
056: BeanFactoryAware {
057:
058: private String targetEditorId;
059:
060: private String rootEditorId;
061:
062: private String rootProperty;
063:
064: private String rootIdAttribute;
065:
066: private String[] display;
067:
068: private BeanFactory beanFactory;
069:
070: private EditorRepository editorRepository;
071:
072: private ListDefinition rootListDefinition;
073:
074: private EditorDefinition targetEditorDefinition;
075:
076: private ListConfig targetListConfig;
077:
078: /**
079: * Sets the id of an editor that will be used to list the target objects.
080: * If the specified editor is nested within another list, Riot will display
081: * the root list and let the user descend down to the target list.
082: */
083: public void setTargetEditorId(String targetEditorId) {
084: this .targetEditorId = targetEditorId;
085: }
086:
087: /**
088: * Sets the id of the list that that is initially displayed. When you
089: * specify a nested {@link #setTargetEditorId(String) target editor}, the
090: * choosing process will start with the root list.
091: * <p>
092: * This property allows you to select another list as starting point.
093: * Since any non-root list will require a parent id, you have to specify a
094: * {@link #setRootProperty(String) root property} or a
095: * {@link #setRootIdAttribute(String) root id attribute}.
096: */
097: public void setRootEditorId(String rootEditorId) {
098: this .rootEditorId = rootEditorId;
099: }
100:
101: /**
102: * Sets the name of a property on the <b>parent</b> object that refers to
103: * the parent object of the {@link #setRootEditorId(String) root list}.
104: * <p>
105: * <b>Example:</b> Lets say we had three classes (Foo, Bar, Baz) and the Riot navigation
106: * path looked like this:
107: * <pre>
108: * Foo: foo1 | Bar: bar1 | Baz: new
109: * </pre>
110: * The Baz class has a property "otherBaz" and we want to configure a
111: * chooser that lets the user select another Baz object with the same
112: * parent (<code>foo1</code>). The configuration would look like this:
113: * <pre>
114: * <riot:chooser bind="otherBaz" rootEditorId="bars" rootProperty="foo" />
115: * </pre>
116: * This instructs Riot to invoke the <code>getFoo()</code> method on the
117: * <code>bar1</code> object and use the returned value (<code>foo1</code>)
118: * as parent for the "bars" list.
119: */
120: public void setRootProperty(String rootProperty) {
121: this .rootProperty = rootProperty;
122: }
123:
124: /**
125: * Sets the name of a {@link Form#getAttribute(String) form attribute} that
126: * contains the String which should be used as parentId for the root list.
127: */
128: public void setRootIdAttribute(String rootIdAttribute) {
129: this .rootIdAttribute = rootIdAttribute;
130: }
131:
132: public void setDisplay(String display) {
133: this .display = StringUtils
134: .commaDelimitedListToStringArray(display);
135: }
136:
137: public void setBeanFactory(BeanFactory beanFactory)
138: throws BeansException {
139: this .beanFactory = beanFactory;
140: }
141:
142: protected void afterFormSet() {
143: if (editorRepository == null) {
144: Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
145: "Not a ListableBeanFactory");
146:
147: ListableBeanFactory lbf = (ListableBeanFactory) beanFactory;
148: editorRepository = (EditorRepository) BeanFactoryUtils
149: .beanOfTypeIncludingAncestors(lbf,
150: EditorRepository.class);
151:
152: Assert.notNull(editorRepository,
153: "No EditorRepository found in BeanFactory");
154: }
155:
156: log.debug("Looking up editor: " + targetEditorId);
157: targetEditorDefinition = editorRepository
158: .getEditorDefinition(targetEditorId);
159:
160: Assert.notNull(targetEditorDefinition,
161: "No such EditorDefinition: " + targetEditorId);
162:
163: targetListConfig = EditorDefinitionUtils.getListDefinition(
164: targetEditorDefinition).getListConfig();
165:
166: if (rootEditorId != null) {
167: rootListDefinition = editorRepository
168: .getListDefinition(rootEditorId);
169: Assert.notNull(rootListDefinition,
170: "No such ListDefinition: " + rootEditorId);
171: } else {
172: rootListDefinition = EditorDefinitionUtils
173: .getRootListDefinition(targetEditorDefinition);
174: }
175: }
176:
177: public RiotDao getRiotDao() {
178: return EditorDefinitionUtils.getListDefinition(
179: targetEditorDefinition).getListConfig().getDao();
180: }
181:
182: protected Object loadBean(String objectId) {
183: return EditorDefinitionUtils.loadBean(targetEditorDefinition,
184: objectId);
185: }
186:
187: protected void renderLabel(Object object, PrintWriter writer) {
188: if (object == null) {
189: return;
190: }
191: if (display == null) {
192: new TagWriter(writer).start(Html.SPAN).body(
193: targetEditorDefinition.getLabel(object)).end();
194:
195: return;
196: }
197:
198: DocumentWriter doc = new DocumentWriter(writer);
199: doc.start(Html.SPAN).attribute(Html.COMMON_CLASS,
200: "chosen " + targetListConfig.getId());
201:
202: LabelRenderContext context = new LabelRenderContext();
203: CellRenderer defaultRenderer = editorRepository
204: .getListRepository().getDefaultCellRenderer();
205:
206: ProtectedBeanWrapper wrapper = new ProtectedBeanWrapper(object);
207: for (int i = 0; i < display.length; i++) {
208: ColumnConfig column = targetListConfig
209: .getColumnConfig(display[i]);
210: CellRenderer renderer = null;
211: if (column != null) {
212: renderer = column.getRenderer();
213: }
214: if (renderer == null) {
215: renderer = defaultRenderer;
216: }
217: Object value = wrapper.getPropertyValue(display[i]);
218: doc.start(Html.SPAN).attribute(Html.COMMON_CLASS,
219: FormatUtils.toCssClass(display[i])).body();
220:
221: renderer.render(display[i], value, context, writer);
222: doc.end();
223: }
224:
225: doc.end();
226: }
227:
228: protected String getChooserUrl() {
229: String rootId = null;
230: if (rootProperty != null) {
231: Object parent = FormUtils.loadParent(getForm());
232: Object root = PropertyUtils.getProperty(parent,
233: rootProperty);
234: rootId = EditorDefinitionUtils.getObjectId(
235: rootListDefinition, root);
236: } else if (rootIdAttribute != null) {
237: rootId = (String) getForm().getAttribute(rootIdAttribute);
238: }
239: return rootListDefinition.getEditorUrl(null, rootId)
240: + "?choose=" + targetEditorDefinition.getId();
241: }
242:
243: private class LabelRenderContext implements RenderContext {
244:
245: public String getListId() {
246: return targetListConfig.getId();
247: }
248:
249: public Class getBeanClass() {
250: return targetEditorDefinition.getBeanClass();
251: }
252:
253: public String getContextPath() {
254: return getFormContext().getContextPath();
255: }
256:
257: public MessageResolver getMessageResolver() {
258: return getFormContext().getMessageResolver();
259: }
260:
261: }
262:
263: }
|