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

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

There is a newer version: 9.0.1
Show newest version
// ***************************************************************************************************************************
// * 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.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.
 */
public class XmlBeanMeta extends BeanMetaExtended {

	// 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.
	 */
	public XmlBeanMeta(BeanMeta beanMeta) {
		super(beanMeta);

		Class c = beanMeta.getClassMeta().getInnerClass();
		XmlBeanMetaBuilder b = new XmlBeanMetaBuilder(beanMeta);

		attrs = Collections.unmodifiableMap(b.attrs);
		elements = Collections.unmodifiableMap(b.elements);
		attrsProperty = b.attrsProperty;
		collapsedProperties = Collections.unmodifiableMap(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 = new LinkedHashMap(),
			elements = new LinkedHashMap(),
			collapsedProperties = new LinkedHashMap();
		BeanPropertyMeta
			attrsProperty,
			contentProperty;
		XmlFormat contentFormat = DEFAULT;

		XmlBeanMetaBuilder(BeanMeta beanMeta) {
			Class c = beanMeta.getClassMeta().getInnerClass();
			Xml xml = c.getAnnotation(Xml.class);
			XmlFormat defaultFormat = null;

			if (xml != null) {
				XmlFormat xf = xml.format();
				if (xf == ATTRS)
					defaultFormat = XmlFormat.ATTR;
				else if (xf.isOneOf(ELEMENTS, DEFAULT))
					defaultFormat = ELEMENT;
				else if (xf == VOID) {
					contentFormat = VOID;
					defaultFormat = 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", xml.format());
			}

			for (BeanPropertyMeta p : beanMeta.getPropertyMetas()) {
				XmlFormat xf = p.getExtendedMeta(XmlBeanPropertyMeta.class).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 == 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 = p.getExtendedMeta(XmlBeanPropertyMeta.class).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.
	 */
	protected 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()}
	 *
	 * @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.
	 */
	protected 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. */ protected 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 - 2024 Weber Informatics LLC | Privacy Policy