All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.xml.XmlBeanMeta Maven / Gradle / Ivy

// ***************************************************************************************************************************
// * 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.juneau.xml;

import static org.apache.juneau.internal.CollectionUtils.*;
import static org.apache.juneau.xml.annotation.XmlFormat.*;

import java.util.*;

import org.apache.juneau.*;
import org.apache.juneau.xml.annotation.*;

/**
 * Metadata on beans specific to the XML serializers and parsers pulled from the {@link Xml @Xml} annotation on the
 * class.
 *
 * 
See Also:
*/ public class XmlBeanMeta extends ExtendedBeanMeta { // XML related fields private final Map attrs; // Map of bean properties that are represented as XML attributes. private final Map elements; // Map of bean properties that are represented as XML elements. private final BeanPropertyMeta attrsProperty; // Bean property that contain XML attribute key/value pairs for this bean. private final Map collapsedProperties; // Properties defined with @Xml.childName annotation. private final BeanPropertyMeta contentProperty; private final XmlFormat contentFormat; /** * Constructor. * * @param beanMeta The metadata on the bean that this metadata applies to. * @param mp XML metadata provider (for finding information about other artifacts). */ public XmlBeanMeta(BeanMeta beanMeta, XmlMetaProvider mp) { super(beanMeta); Class c = beanMeta.getClassMeta().getInnerClass(); XmlBeanMetaBuilder b = new XmlBeanMetaBuilder(beanMeta, mp); attrs = unmodifiable(b.attrs); elements = unmodifiable(b.elements); attrsProperty = b.attrsProperty; collapsedProperties = unmodifiable(b.collapsedProperties); contentProperty = b.contentProperty; contentFormat = b.contentFormat; // Do some validation. if (contentProperty != null || contentFormat == XmlFormat.VOID) { if (! elements.isEmpty()) throw new BeanRuntimeException(c, "{0} and ELEMENT properties found on the same bean. These cannot be mixed.", contentFormat); if (! collapsedProperties.isEmpty()) throw new BeanRuntimeException(c, "{0} and COLLAPSED properties found on the same bean. These cannot be mixed.", contentFormat); } if (! collapsedProperties.isEmpty()) { if (! Collections.disjoint(elements.keySet(), collapsedProperties.keySet())) throw new BeanRuntimeException(c, "Child element name conflicts found with another property."); } } private static class XmlBeanMetaBuilder { Map attrs = map(), elements = map(), collapsedProperties = map(); BeanPropertyMeta attrsProperty, contentProperty; XmlFormat contentFormat = DEFAULT; XmlBeanMetaBuilder(BeanMeta beanMeta, XmlMetaProvider mp) { Class c = beanMeta.getClassMeta().getInnerClass(); Value defaultFormat = Value.empty(); mp.forEachAnnotation(Xml.class, c, x-> true, x -> { XmlFormat xf = x.format(); if (xf == ATTRS) defaultFormat.set(XmlFormat.ATTR); else if (xf.isOneOf(ELEMENTS, DEFAULT)) defaultFormat.set(ELEMENT); else if (xf == VOID) { contentFormat = VOID; defaultFormat.set(VOID); } else throw new BeanRuntimeException(c, "Invalid format specified in @Xml annotation on bean: {0}. Must be one of the following: DEFAULT,ATTRS,ELEMENTS,VOID", x.format()); }); beanMeta.forEachProperty(null, p -> { XmlFormat xf = mp.getXmlBeanPropertyMeta(p).getXmlFormat(); ClassMeta pcm = p.getClassMeta(); if (xf == ATTR) { attrs.put(p.getName(), p); } else if (xf == ELEMENT) { elements.put(p.getName(), p); } else if (xf == COLLAPSED) { collapsedProperties.put(p.getName(), p); } else if (xf == DEFAULT) { if (defaultFormat.get() == ATTR) attrs.put(p.getName(), p); else elements.put(p.getName(), p); } else if (xf == ATTRS) { if (attrsProperty != null) throw new BeanRuntimeException(c, "Multiple instances of ATTRS properties defined on class. Only one property can be designated as such."); if (! pcm.isMapOrBean()) throw new BeanRuntimeException(c, "Invalid type for ATTRS property. Only properties of type Map and bean can be used."); attrsProperty = p; } else if (xf.isOneOf(ELEMENTS, MIXED, MIXED_PWS, TEXT, TEXT_PWS, XMLTEXT)) { if (xf.isOneOf(ELEMENTS, MIXED, MIXED_PWS) && ! pcm.isCollectionOrArray()) throw new BeanRuntimeException(c, "Invalid type for {0} property. Only properties of type Collection and array can be used.", xf); if (contentProperty != null) { if (xf == contentFormat) throw new BeanRuntimeException(c, "Multiple instances of {0} properties defined on class. Only one property can be designated as such.", xf); throw new BeanRuntimeException(c, "{0} and {1} properties found on the same bean. Only one property can be designated as such.", contentFormat, xf); } contentProperty = p; contentFormat = xf; } // Look for any properties that are collections with @Xml.childName specified. String n = mp.getXmlBeanPropertyMeta(p).getChildName(); if (n != null) { if (collapsedProperties.containsKey(n) && collapsedProperties.get(n) != p) throw new BeanRuntimeException(c, "Multiple properties found with the child name ''{0}''.", n); collapsedProperties.put(n, p); } }); } } /** * The list of properties that should be rendered as XML attributes. * * @return Map of property names to property metadata. */ public Map getAttrProperties() { return attrs; } /** * The list of names of properties that should be rendered as XML attributes. * * @return Set of property names. */ protected Set getAttrPropertyNames() { return attrs.keySet(); } /** * The list of properties that should be rendered as child elements. * * @return Map of property names to property metadata. */ protected Map getElementProperties() { return elements; } /** * The list of names of properties that should be rendered as child elements. * * @return Set of property names. */ protected Set getElementPropertyNames() { return elements.keySet(); } /** * The list of properties that should be rendered as collapsed child elements. *
See {@link Xml#childName() @Xml(childName)} * * @return Map of property names to property metadata. */ protected Map getCollapsedProperties() { return collapsedProperties; } /** * The list of names of properties that should be rendered as collapsed child elements. * * @return Set of property names. */ protected Set getCollapsedPropertyNames() { return collapsedProperties.keySet(); } /** * The property that returns a map of XML attributes as key/value pairs. * * @return The bean property metadata, or null if there is no such method. */ protected BeanPropertyMeta getAttrsProperty() { return attrsProperty; } /** * The name of the property that returns a map of XML attributes as key/value pairs. * * @return The bean property name, or null if there is no such method. */ protected String getAttrsPropertyName() { return attrsProperty == null ? null : attrsProperty.getName(); } /** * The property that represents the inner XML content of this bean. * * @return The bean property metadata, or null if there is no such method. */ public BeanPropertyMeta getContentProperty() { return contentProperty; } /** * The name of the property that represents the inner XML content of this bean. * * @return The bean property name, or null if there is no such method. */ protected String getContentPropertyName() { return contentProperty == null ? null : contentProperty.getName(); } /** * Returns the format of the inner XML content of this bean. * *

* Can be one of the following: *

    *
  • {@link XmlFormat#ELEMENTS} *
  • {@link XmlFormat#MIXED} *
  • {@link XmlFormat#MIXED_PWS} *
  • {@link XmlFormat#TEXT} *
  • {@link XmlFormat#TEXT_PWS} *
  • {@link XmlFormat#XMLTEXT} *
  • {@link XmlFormat#VOID} *
  • null *
* * @return The format of the inner XML content of this bean. */ public XmlFormat getContentFormat() { return contentFormat; } /** * Returns bean property meta with the specified name. * *

* This is identical to calling {@link BeanMeta#getPropertyMeta(String)} except it first retrieves the bean property * meta based on the child name (e.g. a property whose name is "people", but whose child name is "person"). * * @param fieldName The bean property name. * @return The property metadata. */ protected BeanPropertyMeta getPropertyMeta(String fieldName) { if (collapsedProperties != null) { BeanPropertyMeta bpm = collapsedProperties.get(fieldName); if (bpm == null) bpm = collapsedProperties.get("*"); if (bpm != null) return bpm; } return getBeanMeta().getPropertyMeta(fieldName); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy