/* Copyright (c) 2008 Google Inc. * * Licensed 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 com.google.gdata.wireformats; import com.google.gdata.util.common.xml.XmlNamespace; import com.google.gdata.client.CoreErrorDomain; import com.google.gdata.model.AttributeKey; import com.google.gdata.model.Element; import com.google.gdata.model.ElementKey; import com.google.gdata.model.ElementMetadata; import com.google.gdata.model.ElementMetadata.Cardinality; import com.google.gdata.model.QName; import com.google.gdata.model.ValidationContext; import com.google.gdata.util.ParseException; import org.xml.sax.Attributes; import java.io.IOException; import java.util.List; /** * XML handler that translates XML content from a wire format into * an in-memory representation. */ public class XmlHandler extends XmlParser.ElementHandler { /** * Validation context, used to accumulate metadata validation * errors discovered during parsing. */ protected final ValidationContext vc; /** * Parent element, if non-null the element will be added to the parent after * it has been fully parsed. */ protected final Element parentElement; /** Metadata for this element. */ protected final ElementMetadata<?, ?> metadata; /** * Element being parsed. */ protected final Element element; /** * Construct an xml parser that will add the element to its parent after * parsing is completed. */ public XmlHandler(ValidationContext vc, Element parent, Element element, ElementMetadata<?, ?> metadata) { this.vc = vc; this.parentElement = parent; this.element = element; this.metadata = metadata; } /** * @return element that was parsed */ public Element getElement() { return element; } @Override public void processAttribute(QName qn, String value) throws ParseException { if (element.hasAttribute(qn)) { throw new ParseException( CoreErrorDomain.ERR.duplicateAttributeValue.withInternalReason( "Duplicate value for attribute " + qn)); } AttributeKey<?> attKey = (metadata == null) ? null : metadata.findAttribute(qn); if (attKey != null) { element.setAttributeValue(attKey, ObjectConverter.getValue(value, attKey.getDatatype())); } else { element.setAttributeValue(qn, value); } } /** * Default child handler for xml. This will parse into a {@link Element} if * the element has not been declared, otherwise it will parse into the type * defined by the metadata. * * @throws ParseException if a non-repeating element is repeated, or if the * element type requested cannot be created. * @throws IOException from overriding code. Not thrown by the default * implementation. */ @Override public XmlHandler getChildHandler(QName qName, Attributes attrs, List<XmlNamespace> namespaces) throws ParseException, IOException { ElementMetadata<?, ?> childMeta = findMetadata(qName); Element childElement = createChildElement(qName, childMeta); // "SET" cardinality elements cannot be added to the parent element until // fully initialized, otherwise we'll have duplicates. So we track the // parent element and add to it after the element has been processed. Element parent; if (childMeta != null && childMeta.getCardinality() == Cardinality.SET) { parent = element; } else { element.addElement(childElement); parent = null; } return createHandler(qName, parent, childElement, childMeta); } /** * Hook to allow subclasses to change the type of handler being returned. */ protected XmlHandler createHandler(QName qName, Element parent, Element child, ElementMetadata<?, ?> metadata) { return new XmlHandler(vc, parent, child, metadata); } /** * Returns the appropriate metadata to use for the given qualified name. */ protected ElementMetadata<?, ?> findMetadata(QName qName) { if (metadata == null) { return null; } ElementKey<?, ?> childKey = metadata.findElement(qName); if (childKey == null) { return null; } ElementMetadata<?, ?> childMeta = metadata.bindElement(childKey); return childMeta; } /** * Create a child element for the given name and metadata. */ protected Element createChildElement(QName qName, ElementMetadata<?, ?> metadata) throws ParseException { if (metadata == null) { return new Element(qName); } try { return metadata.createElement(); } catch (ContentCreationException e) { // to ElementHandler interface? throw new ParseException(e); } } @Override public void processEndElement() throws ParseException { if (value != null) { value = value.trim(); if (!value.equals("")) { ElementKey<?, ?> elementKey = element.getElementKey(); element.setTextValue( ObjectConverter.getValue(value, elementKey.getDatatype())); } } if (parentElement != null) { parentElement.addElement(element); } } }