001: /*
002: * Copyright 2004 Outerthought bvba and Schaubroeck nv
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * 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, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package org.outerj.daisy.frontend.editor;
017:
018: import org.outerj.daisy.repository.schema.PartTypeUse;
019: import org.outerj.daisy.repository.Repository;
020: import org.outerj.daisy.repository.Document;
021: import org.outerj.daisy.repository.Part;
022: import org.outerj.daisy.frontend.util.FormHelper;
023: import org.outerj.daisy.frontend.util.MultiMessage;
024: import org.outerj.daisy.frontend.util.TaggedMessage;
025: import org.outerj.daisy.xmlutil.LocalSAXParserFactory;
026: import org.apache.cocoon.forms.formmodel.Form;
027: import org.apache.cocoon.forms.formmodel.Widget;
028: import org.apache.cocoon.forms.validation.WidgetValidator;
029: import org.apache.cocoon.forms.validation.ValidationErrorAware;
030: import org.apache.cocoon.forms.validation.ValidationError;
031: import org.apache.avalon.framework.service.ServiceManager;
032: import org.apache.avalon.framework.context.Context;
033: import org.xml.sax.*;
034:
035: import java.util.Map;
036: import java.util.List;
037: import java.util.ArrayList;
038: import java.io.StringReader;
039:
040: public class VariablesPartEditor implements PartEditor {
041: private ServiceManager serviceManager;
042:
043: private VariablesPartEditor(ServiceManager serviceManager) {
044: this .serviceManager = serviceManager;
045: }
046:
047: public static class Factory implements PartEditorFactory {
048: public PartEditor getPartEditor(Map properties,
049: ServiceManager serviceManager, Context context) {
050: return new VariablesPartEditor(serviceManager);
051: }
052: }
053:
054: public Form getForm(PartTypeUse partTypeUse,
055: DocumentEditorForm documentEditorForm, Repository repository)
056: throws Exception {
057: Form form = FormHelper.createForm(serviceManager,
058: "resources/form/parteditor_variables_definition.xml");
059: form.getChild("variables").addValidator(
060: new PartRequiredValidator(documentEditorForm,
061: partTypeUse.isRequired(), false));
062: form.getChild("variables").addValidator(
063: new VariablesValidator());
064: return form;
065: }
066:
067: public String getFormTemplate() {
068: return "resources/form/parteditor_variables_template.xml";
069: }
070:
071: public void load(Form form, Document document, Part part,
072: Repository repository) throws Exception {
073: PartEditorHelper.load(form, part, "variables");
074: }
075:
076: public void save(Form form, Document document) throws Exception {
077: PartEditorHelper.save(form, document, "variables", "text/xml");
078: }
079:
080: private static class VariablesValidator implements WidgetValidator {
081: public boolean validate(Widget widget) {
082: boolean success = true;
083: String value = (String) widget.getValue();
084: if (value != null) {
085: List<Error> errors = new ArrayList<Error>();
086: XMLReader reader;
087: try {
088: reader = LocalSAXParserFactory.newXmlReader();
089: } catch (Exception e) {
090: throw new RuntimeException("Unexpected error.", e);
091: }
092: InputSource is = new InputSource(
093: new StringReader(value));
094: reader
095: .setContentHandler(new VariablesValidationHandler(
096: errors));
097: reader
098: .setErrorHandler(new VariablesErrorHandler(
099: errors));
100: try {
101: reader.parse(is);
102: } catch (SAXParseException e) {
103: // will be reported via error handler
104: } catch (Exception e) {
105: errors.add(new Error(e.getMessage()));
106: }
107:
108: if (errors.size() > 0) {
109: success = false;
110:
111: MultiMessage message = new MultiMessage();
112: message.addMessage(new TaggedMessage("title",
113: "Error parsing or validating XML:"));
114: for (int i = 0; i < errors.size(); i++) {
115: if (i > 6 && errors.size() > 9) {
116: message.addMessage(new TaggedMessage(
117: "error", "... "
118: + (errors.size() - i)
119: + " more errors"));
120: break;
121: }
122: Error error = errors.get(i);
123: message.addMessage(new TaggedMessage("error",
124: error.toString()));
125: }
126: ((ValidationErrorAware) widget)
127: .setValidationError(new ValidationError(
128: message));
129: }
130: }
131: return success;
132: }
133: }
134:
135: private static class Error {
136: private int line;
137: private int column;
138: private String message;
139:
140: public Error(int line, int column, String message) {
141: this .line = line;
142: this .column = column;
143: this .message = message;
144: }
145:
146: public Error(Locator locator, String message) {
147: this .line = locator != null ? locator.getLineNumber() : -1;
148: this .column = locator != null ? locator.getColumnNumber()
149: : -1;
150: this .message = message;
151: }
152:
153: public Error(String message) {
154: this .line = -1;
155: this .column = -1;
156: this .message = message;
157: }
158:
159: public String toString() {
160: return line + ":" + column + " - " + message;
161: }
162: }
163:
164: private static class VariablesValidationHandler implements
165: ContentHandler {
166: private List<Error> errors;
167: private Locator locator;
168: private int elementNesting = 0;
169: private final String NS = "http://outerx.org/daisy/1.0#variables";
170:
171: public VariablesValidationHandler(List<Error> errors) {
172: this .errors = errors;
173: }
174:
175: public void setDocumentLocator(Locator locator) {
176: this .locator = locator;
177: }
178:
179: public void startElement(String uri, String localName,
180: String qName, Attributes atts) throws SAXException {
181: elementNesting++;
182:
183: if (elementNesting == 1) {
184: if (!uri.equals(NS) || !localName.equals("variables")) {
185: errors.add(new Error(locator,
186: "Root element should be named <variables> in namespace "
187: + NS));
188: }
189: } else if (elementNesting == 2) {
190: if (uri.equals(NS) && !localName.equals("variable")) {
191: errors.add(new Error(locator,
192: "Expected a <variable> element in namespace "
193: + NS));
194: } else if (uri.equals(NS)
195: && localName.equals("variable")
196: && atts.getValue("name") == null) {
197: errors.add(new Error(locator,
198: "Missing name attribute."));
199: }
200: }
201: }
202:
203: public void endElement(String uri, String localName,
204: String qName) throws SAXException {
205: elementNesting--;
206: }
207:
208: public void startDocument() throws SAXException {
209: }
210:
211: public void endDocument() throws SAXException {
212: }
213:
214: public void startPrefixMapping(String prefix, String uri)
215: throws SAXException {
216: }
217:
218: public void endPrefixMapping(String prefix) throws SAXException {
219: }
220:
221: public void characters(char ch[], int start, int length)
222: throws SAXException {
223: }
224:
225: public void ignorableWhitespace(char ch[], int start, int length)
226: throws SAXException {
227: }
228:
229: public void processingInstruction(String target, String data)
230: throws SAXException {
231: }
232:
233: public void skippedEntity(String name) throws SAXException {
234: }
235: }
236:
237: private static class VariablesErrorHandler implements ErrorHandler {
238: private List<Error> errors;
239:
240: public VariablesErrorHandler(List<Error> errors) {
241: this .errors = errors;
242: }
243:
244: public void warning(SAXParseException exception)
245: throws SAXException {
246: }
247:
248: public void error(SAXParseException exception)
249: throws SAXException {
250: errors.add(new Error(exception.getLineNumber(), exception
251: .getColumnNumber(), exception.getMessage()));
252: }
253:
254: public void fatalError(SAXParseException exception)
255: throws SAXException {
256: errors.add(new Error(exception.getLineNumber(), exception
257: .getColumnNumber(), exception.getMessage()));
258: }
259: }
260: }
|