001: /*
002: * Copyright 2008 Outerthought bvba and Schaubroeck nv
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.outerj.daisy.frontend.editor;
017:
018: import java.util.ArrayList;
019: import java.util.HashMap;
020: import java.util.List;
021: import java.util.Locale;
022: import java.util.Map;
023:
024: import org.apache.avalon.framework.context.Context;
025: import org.apache.avalon.framework.logger.Logger;
026: import org.apache.avalon.framework.service.ServiceManager;
027: import org.apache.cocoon.components.LifecycleHelper;
028: import org.apache.cocoon.components.flow.util.PipelineUtil;
029: import org.apache.cocoon.forms.formmodel.ContainerWidget;
030: import org.apache.cocoon.forms.formmodel.Widget;
031: import org.apache.cocoon.forms.validation.ValidationError;
032: import org.apache.cocoon.forms.validation.ValidationErrorAware;
033: import org.apache.cocoon.xml.IncludeXMLConsumer;
034: import org.apache.xmlbeans.XmlCursor;
035: import org.outerj.daisy.frontend.util.GenericPipeConfig;
036: import org.outerj.daisy.frontend.util.XmlObjectXMLizable;
037: import org.outerj.daisy.repository.Document;
038: import org.outerj.daisy.repository.HierarchyPath;
039: import org.outerj.daisy.repository.Repository;
040: import org.outerj.daisy.repository.ValueType;
041: import org.outerj.daisy.repository.schema.FieldType;
042: import org.outerj.daisy.repository.schema.FieldTypeUse;
043: import org.outerj.daisy.repository.variant.VariantManager;
044: import org.outerx.daisy.x10.FieldTypeUseDocument;
045: import org.xml.sax.ContentHandler;
046:
047: /**
048: * A base for simple implementations of the
049: * {@link org.outerj.daisy.frontend.editor.FieldEditor FieldEditor} interface.
050: *
051: */
052: public abstract class AbstractFieldEditor implements FieldEditor {
053:
054: protected FieldTypeUse fieldTypeUse;
055:
056: protected FieldType fieldType;
057:
058: protected Widget widget;
059:
060: protected Repository repository;
061:
062: protected Context context;
063:
064: protected ServiceManager serviceManager;
065:
066: protected Logger logger;
067:
068: public AbstractFieldEditor(FieldTypeUse fieldTypeUse,
069: ServiceManager serviceManager, Context context,
070: Logger logger) {
071: this .fieldTypeUse = fieldTypeUse;
072: this .fieldType = fieldTypeUse.getFieldType();
073: this .serviceManager = serviceManager;
074: this .context = context;
075: this .logger = logger;
076: }
077:
078: public abstract void load(Document document) throws Exception;
079:
080: /**
081: * This function is called by the
082: * {@link #generateFormDefinitionFragment(ContentHandler, Locale)} method.
083: * This function should always turn the correct path of definition template.
084: *
085: * @return String containing the location of the widget definition template.
086: */
087: public abstract String getDefinitionTemplate();
088:
089: /**
090: * This function is called by the
091: * {@link #generateFormDefinitionFragment(ContentHandler, Locale)} method.
092: * This function should return the correct path of a stylesheet or null.
093: *
094: * @return String containing the location of the stylesheet that should
095: * transform the output of the widget definition template.
096: */
097: public abstract String getDefinitionStylesheet();
098:
099: /**
100: * This function is called by the
101: * {@link #generateFormTemplateFragment(ContentHandler, Locale)} method. *
102: * This function should always turn the correct path of a template.
103: *
104: * @return String containing the location of the template that generates the
105: * widget template.
106: */
107: public abstract String getTemplateTemplate();
108:
109: /**
110: * This function is called by the
111: * {@link #generateFormTemplateFragment(ContentHandler, Locale)} method.
112: * This function should return the correct path of a stylesheet or null.
113: *
114: * @return String containing the location of the
115: */
116: public abstract String getTemplateStylesheet();
117:
118: /**
119: * This method generates sax events using the internal/GenericPipe found in
120: * the sitemap. The template & stylesheet used are specified by implementing
121: * the {@link #getDefinitionTemplate()} and
122: * {@link #getDefinitionStylesheet()} methods.
123: */
124: public void generateFormDefinitionFragment(
125: ContentHandler contentHandler, Locale locale)
126: throws Exception {
127: GenericPipeConfig pipeConfig = new GenericPipeConfig();
128: pipeConfig.setApplyI18n(true);
129: pipeConfig.setApplyLayout(false);
130: pipeConfig.setTransformLinks(false);
131: pipeConfig.setXmlSerializer();
132:
133: pipeConfig.setTemplate(this .getDefinitionTemplate());
134: String stylesheet = this .getDefinitionStylesheet();
135: if (stylesheet != null && stylesheet.length() > 0)
136: pipeConfig.setStylesheet(stylesheet);
137:
138: processGenericPipe(pipeConfig, null, contentHandler, locale);
139: }
140:
141: /**
142: * This method generates sax events using the internal/GenericPipe found in
143: * the sitemap. The template & stylesheet used are specified by implementing
144: * the {@link #getTemplateTemplate()} and {@link #getTemplateStylesheet()}
145: * methods.
146: */
147: public void generateFormTemplateFragment(
148: ContentHandler contentHandler, Locale locale)
149: throws Exception {
150: GenericPipeConfig pipeConfig = new GenericPipeConfig();
151: pipeConfig.setApplyI18n(true);
152: pipeConfig.setApplyLayout(false);
153: pipeConfig.setTransformLinks(false);
154: pipeConfig.setXmlSerializer();
155:
156: pipeConfig.setTemplate(this .getTemplateTemplate());
157: String stylesheet = this .getTemplateStylesheet();
158: if (stylesheet != null && stylesheet.length() > 0)
159: pipeConfig.setStylesheet(stylesheet);
160:
161: processGenericPipe(pipeConfig, null, contentHandler, locale);
162: }
163:
164: protected void processGenericPipe(GenericPipeConfig pipeConfig,
165: Map<String, Object> viewData,
166: ContentHandler contentHandler, Locale locale)
167: throws Exception {
168: PipelineUtil pipelineUtil = new PipelineUtil();
169: try {
170: LifecycleHelper.setupComponent(pipelineUtil, logger,
171: context, serviceManager, null, false);
172: if (viewData == null)
173: viewData = new HashMap<String, Object>();
174: viewData.put("pipeConf", pipeConfig);
175: viewData.put("fieldTypeUse", fieldTypeUse);
176: viewData.put("fieldTypeUseXml", new XmlObjectXMLizable(
177: getAnnotatedFieldTypeUseXml(locale)));
178: pipelineUtil.processToSAX("internal/genericPipe", viewData,
179: new IncludeXMLConsumer(contentHandler));
180: } finally {
181: LifecycleHelper.dispose(pipelineUtil);
182: }
183: }
184:
185: public FieldTypeUse getFieldTypeUse() {
186: return fieldTypeUse;
187: }
188:
189: public boolean hasValue(Widget parentWidget) {
190: Widget widget = ((ContainerWidget) parentWidget)
191: .getChild("field");
192: if (widget.getValue() == null && widget.validate()) {
193: return false;
194: } else if (fieldType.isMultiValue() && widget.validate()) {
195: Object[] values = (Object[]) widget.getValue();
196: if (values.length == 0)
197: return false;
198: }
199: return true;
200: }
201:
202: public void init(Widget parentWidget,
203: FieldEditorContext fieldEditorContext) {
204: this .widget = ((ContainerWidget) parentWidget)
205: .getChild("field");
206: this .repository = fieldEditorContext.getRepository();
207:
208: }
209:
210: public void setValidationError(ValidationError error) {
211: ValidationErrorAware valueWidget = (ValidationErrorAware) widget;
212: valueWidget.setValidationError(error);
213: }
214:
215: public void store(Document document) throws Exception {
216: long fieldTypeId = fieldType.getId();
217: Object value = widget.getValue();
218: if (value == null) {
219: document.deleteField(fieldTypeId);
220: } else if (value instanceof Object[]) {
221: Object[] values = (Object[]) value;
222: if (values.length > 0)
223: document.setField(fieldTypeId, getValueToSave(values,
224: fieldType, repository));
225: else
226: document.deleteField(fieldTypeId);
227: } else {
228: document.setField(fieldTypeId, getValueToSave(value,
229: fieldType, repository));
230: }
231: }
232:
233: protected final FieldTypeUseDocument getAnnotatedFieldTypeUseXml(
234: Locale locale) {
235: FieldType fieldType = fieldTypeUse.getFieldType();
236: FieldTypeUseDocument fieldTypeUseDoc = fieldTypeUse
237: .getExtendedXml();
238: String label = fieldType.getLabel(locale);
239: String description = fieldType.getDescription(locale);
240: XmlCursor cursor = fieldTypeUseDoc.getFieldTypeUse()
241: .getFieldType().newCursor();
242: while (!cursor.isStart())
243: cursor.toNextToken();
244: cursor.toNextToken();
245: cursor.insertAttributeWithValue("label", label);
246: if (description != null)
247: cursor.insertAttributeWithValue("description", description);
248: cursor.dispose();
249: return fieldTypeUseDoc;
250: }
251:
252: protected Object getValueToSave(Object value, FieldType fieldType,
253: Repository repository) {
254: if (fieldType.isHierarchical() && fieldType.isMultiValue()) {
255: value = convertStringsToHierarchyPaths((Object[]) value,
256: fieldType.getValueType(), repository);
257: } else if (fieldType.isHierarchical()) {
258: value = convertStringToHierarchyPath((String) value,
259: fieldType.getValueType(), repository);
260: } else if (fieldType.getValueType() == ValueType.LINK) {
261: VariantManager variantManager = repository
262: .getVariantManager();
263: if (value instanceof Object[]) {
264: Object[] values = (Object[]) value;
265: Object[] newValues = new Object[values.length];
266: for (int i = 0; i < values.length; i++)
267: newValues[i] = LinkFieldHelper.parseVariantKey(
268: (String) values[i], variantManager);
269: value = newValues;
270: } else {
271: value = LinkFieldHelper.parseVariantKey((String) value,
272: repository.getVariantManager());
273: }
274: }
275: return value;
276: }
277:
278: private HierarchyPath[] convertStringsToHierarchyPaths(
279: Object[] values, ValueType valueType, Repository repository) {
280: HierarchyPath[] paths = new HierarchyPath[values.length];
281: for (int i = 0; i < paths.length; i++) {
282: paths[i] = convertStringToHierarchyPath((String) values[i],
283: valueType, repository);
284: }
285: return paths;
286: }
287:
288: private HierarchyPath convertStringToHierarchyPath(String value,
289: ValueType valueType, Repository repository) {
290: String[] parts = HierarchicalFieldHelper
291: .parseHierarchicalInput(value);
292: List<Object> elements = new ArrayList<Object>();
293: for (String part : parts) {
294: if (valueType == ValueType.LINK) {
295: elements.add(LinkFieldHelper.parseVariantKey(part,
296: repository.getVariantManager()));
297: } else if (valueType == ValueType.STRING) {
298: elements.add(part);
299: } else {
300: throw new RuntimeException(
301: "Unexpected valuetype for hierarhical field: "
302: + valueType);
303: }
304: }
305: return new HierarchyPath(elements.toArray());
306: }
307: }
|