/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.woody.datatype; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.Attributes; import org.apache.avalon.framework.service.ServiceManager; import org.apache.excalibur.source.SourceResolver; import org.apache.excalibur.source.Source; import org.apache.cocoon.ProcessingException; import org.apache.cocoon.components.source.SourceUtil; import org.apache.cocoon.xml.AttributesImpl; import org.apache.cocoon.xml.AbstractXMLPipe; import org.apache.cocoon.xml.dom.DOMBuilder; import org.apache.cocoon.woody.Constants; import org.apache.cocoon.woody.datatype.convertor.Convertor; import org.apache.cocoon.woody.datatype.convertor.DefaultFormatCache; import org.w3c.dom.Element; import java.io.IOException; import java.util.Locale; /** * SelectionList implementation that always reads its content from the source * each time it is requested. * * <p>Note: the class {@link SelectionListBuilder} also interprets the same wd:selection-list XML, so if * anything changes here to how that XML is interpreted, it also needs to change over there and vice versa. * * @version $Id$ */ public class DynamicSelectionList implements SelectionList { private String src; private Datatype datatype; private ServiceManager serviceManager; public DynamicSelectionList(Datatype datatype, String src, ServiceManager serviceManager) { this.datatype = datatype; this.src = src; this.serviceManager = serviceManager; } public Datatype getDatatype() { return datatype; } /* * This method is only used by a test case and by the public version * of generateSaxFragment. */ void generateSaxFragment(ContentHandler contentHandler, Locale locale, Source source) throws ProcessingException, SAXException, IOException { SelectionListHandler handler = new SelectionListHandler(locale); handler.setContentHandler(contentHandler); SourceUtil.toSAX(serviceManager, source, null, handler); } public void generateSaxFragment(ContentHandler contentHandler, Locale locale) throws SAXException { SourceResolver sourceResolver = null; Source source = null; try { sourceResolver = (SourceResolver)serviceManager.lookup(SourceResolver.ROLE); source = sourceResolver.resolveURI(src); generateSaxFragment(contentHandler, locale, source); } catch (Exception e) { throw new SAXException("Error while generating selection list: " + e.getMessage(), e); } finally { if (sourceResolver != null) { if (source != null) try { sourceResolver.release(source); } catch (Exception e) {} serviceManager.release(sourceResolver); } } } /** * XMLConsumer used to handle selection lists generated on the fly. */ public class SelectionListHandler extends AbstractXMLPipe { private Object currentValue; private String currentValueAsString; private boolean hasLabel; private Locale locale; /** The convertor used to parse the values in the selection list. */ private Convertor convertor; private DOMBuilder convertorConfigDOMBuilder; private int convertorConfigNestingLevel = 0; private Convertor.FormatCache fromFormatCache = new DefaultFormatCache(); private Convertor.FormatCache toFormatCache = new DefaultFormatCache(); public SelectionListHandler(Locale locale) { this.locale = locale; } public void startDocument() throws SAXException { } public void endDocument() throws SAXException { } public void endDTD() throws SAXException { } public void startDTD(String name, String publicId, String systemId) throws SAXException { } public void startElement(String namespaceURI, String localName, String qName, Attributes attributes) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigNestingLevel++; convertorConfigDOMBuilder.startElement(namespaceURI, localName, qName, attributes); } else if (namespaceURI.equals(Constants.WD_NS)) { if (localName.equals("item")) { if (convertor == null) { // if no convertor was explicitely configured, use the default one of the datatype convertor = datatype.getConvertor(); } hasLabel = false; String unparsedValue = attributes.getValue("value"); if (unparsedValue == null || "".equals(unparsedValue)) { // Empty (or null) value translates into the empty string currentValueAsString = ""; } else { currentValue = convertor.convertFromString(unparsedValue, locale, fromFormatCache); if (currentValue == null) { throw new SAXException("Could not interpret the following value: \"" + unparsedValue + "\"."); } currentValueAsString = datatype.getConvertor().convertToString(currentValue, locale, toFormatCache); } AttributesImpl attrs = new AttributesImpl(); attrs.addCDATAAttribute("value", currentValueAsString); super.startElement(Constants.WI_NS, localName, Constants.WI_PREFIX_COLON + localName, attrs); } else if (localName.equals("label")) { hasLabel = true; super.startElement(Constants.WI_NS, localName, Constants.WI_PREFIX_COLON + localName, attributes); } else if (localName.equals("selection-list")) { super.startElement(Constants.WI_NS, localName, Constants.WI_PREFIX_COLON + localName, attributes); } else if (convertor == null && localName.equals("convertor")) { // record the content of this element in a dom-tree convertorConfigDOMBuilder = new DOMBuilder(); convertorConfigDOMBuilder.startElement(namespaceURI, localName, qName, attributes); convertorConfigNestingLevel++; } else { super.startElement(namespaceURI, localName, qName, attributes); } } else { super.startElement(namespaceURI, localName, qName, attributes); } } private static final String LABEL_EL = "label"; public void endElement(String namespaceURI, String localName, String qName) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigNestingLevel--; convertorConfigDOMBuilder.endElement(namespaceURI, localName, qName); if (convertorConfigNestingLevel == 0) { Element convertorElement = convertorConfigDOMBuilder.getDocument().getDocumentElement(); try { convertor = datatype.getBuilder().buildConvertor(convertorElement); } catch (Exception e) { throw new SAXException("Error building convertor from convertor configuration embedded in selection list XML.", e); } } } else if (namespaceURI.equals(Constants.WD_NS)) { if (localName.equals("item")) { if (!hasLabel) { // make the label now super.startElement(Constants.WI_NS, LABEL_EL, Constants.WI_PREFIX_COLON + LABEL_EL, new AttributesImpl()); super.characters(currentValueAsString.toCharArray(), 0, currentValueAsString.length()); super.endElement(Constants.WI_NS, LABEL_EL, Constants.WI_PREFIX_COLON + LABEL_EL); } super.endElement(Constants.WI_NS, localName, Constants.WI_PREFIX_COLON + localName); } else if (localName.equals("label")) { super.endElement(Constants.WI_NS, localName, Constants.WI_PREFIX_COLON + localName); } else if (localName.equals("selection-list")) { super.endElement(Constants.WI_NS, localName, Constants.WI_PREFIX_COLON + localName); } else { super.endElement(namespaceURI, localName, qName); } } else { super.endElement(namespaceURI, localName, qName); } } public void comment(char ch[], int start, int len) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.comment(ch, start, len); } else super.comment(ch, start, len); } public void startPrefixMapping(String prefix, String uri) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.startPrefixMapping(prefix, uri); } else super.startPrefixMapping(prefix, uri); } public void endPrefixMapping(String prefix) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.endPrefixMapping(prefix); } else super.endPrefixMapping(prefix); } public void characters(char c[], int start, int len) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.characters(c, start, len); } else super.characters(c, start, len); } public void ignorableWhitespace(char c[], int start, int len) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.ignorableWhitespace(c, start, len); } else super.ignorableWhitespace(c, start, len); } public void processingInstruction(String target, String data) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.processingInstruction(target, data); } else super.processingInstruction(target, data); } public void skippedEntity(String name) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.skippedEntity(name); } else super.skippedEntity(name); } public void startEntity(String name) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.startEntity(name); } else super.startEntity(name); } public void endEntity(String name) throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.endEntity(name); } else super.endEntity(name); } public void startCDATA() throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.startCDATA(); } else super.startCDATA(); } public void endCDATA() throws SAXException { if (convertorConfigNestingLevel > 0) { convertorConfigDOMBuilder.endCDATA(); } else super.endCDATA(); } } }