/* * JBoss, Home of Professional Open Source * Copyright 2015, Red Hat, Inc., and individual contributors as indicated * by the @authors tag. * * 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 org.jboss.as.controller; import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; import static org.jboss.as.controller.AttributeParsers.ObjectParser.parseEmbeddedElement; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROPERTY; import static org.jboss.as.controller.parsing.ParseUtils.requireAttributes; import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import javax.xml.stream.XMLStreamException; import org.jboss.as.controller.parsing.ParseUtils; import org.jboss.dmr.ModelNode; import org.jboss.staxmapper.XMLExtendedStreamReader; /** * @author Tomaz Cerar (c) 2015 Red Hat Inc. */ public interface AttributeParsers { abstract class MapParser extends AttributeParser { protected final String wrapperElement; protected final String elementName; protected final boolean wrapElement; public MapParser(String wrapperElement, String elementName, boolean wrapElement) { this.wrapperElement = wrapperElement; this.elementName = elementName == null ? PROPERTY : elementName; this.wrapElement = wrapElement; } public MapParser(String wrapperElement, boolean wrapElement) { this(wrapperElement, PROPERTY, wrapElement); } public MapParser(boolean wrapElement) { this(null, null, wrapElement); } public MapParser(String wrapperElement) { this(wrapperElement, null, true); } public MapParser() { this(null, PROPERTY, true); } @Override public boolean isParseAsElement() { return true; } @Override public String getXmlName(AttributeDefinition attribute) { return wrapElement ? wrapperElement != null ? wrapperElement : attribute.getXmlName() : elementName; } @Override public void parseElement(AttributeDefinition attribute, XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException { String wrapper = wrapperElement == null ? attribute.getName() : wrapperElement; assert attribute instanceof MapAttributeDefinition; MapAttributeDefinition mapAttribute = (MapAttributeDefinition) attribute; operation.get(attribute.getName()).setEmptyObject();//create empty attribute to address WFCORE-1448 if (wrapElement) { if (!reader.getLocalName().equals(wrapper)) { throw ParseUtils.unexpectedElement(reader, Collections.singleton(wrapper)); } else { // allow empty properties list if (reader.nextTag() == END_ELEMENT) { return; } } } do { if (elementName.equals(reader.getLocalName())) { //real parsing happens parseSingleElement(mapAttribute, reader, operation); } else { throw ParseUtils.unexpectedElement(reader, Collections.singleton(elementName)); } } while (reader.hasNext() && reader.nextTag() != END_ELEMENT && reader.getLocalName().equals(elementName)); if (wrapElement) { // To exit the do loop either we hit an END_ELEMENT or a START_ELEMENT not for 'elementName' // The latter means a bad document if (reader.getEventType() != END_ELEMENT) { throw ParseUtils.unexpectedElement(reader, Collections.singleton(elementName)); } } } public abstract void parseSingleElement(MapAttributeDefinition attribute, XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException; } static class PropertiesParser extends MapParser { public PropertiesParser(String wrapperElement, String elementName, boolean wrapElement) { super(wrapperElement, elementName, wrapElement); } public PropertiesParser(String wrapperElement, boolean wrapElement) { this(wrapperElement, PROPERTY, wrapElement); } public PropertiesParser(boolean wrapElement) { this(null, null, wrapElement); } public PropertiesParser(String wrapperElement) { this(wrapperElement, null, true); } public PropertiesParser() { this(null, PROPERTY, true); } public void parseSingleElement(MapAttributeDefinition attribute, XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException { final String[] array = requireAttributes(reader, org.jboss.as.controller.parsing.Attribute.NAME.getLocalName(), org.jboss.as.controller.parsing.Attribute.VALUE.getLocalName()); attribute.parseAndAddParameterElement(array[0], array[1], operation, reader); ParseUtils.requireNoContent(reader); } } static class ObjectMapParser extends MapParser { private final String keyAttributeName; //name of attribute to use for "key" public ObjectMapParser(String wrapperElement, String elementName, boolean wrapElement, String keyAttributeName) { super(wrapperElement, elementName, wrapElement); this.keyAttributeName = keyAttributeName == null ? "key" : keyAttributeName; } public ObjectMapParser() { this(null, PROPERTY, true, null); } public ObjectMapParser(boolean wrapElement) { this(null, null, wrapElement, null); } public ObjectMapParser(String elementName, boolean wrapElement) { this(null, elementName, wrapElement, null); } @Override public void parseSingleElement(MapAttributeDefinition attribute, XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException { assert attribute instanceof ObjectMapAttributeDefinition; assert attribute.getParser().isParseAsElement(); ObjectMapAttributeDefinition map = ((ObjectMapAttributeDefinition) attribute); ObjectTypeAttributeDefinition objectType = map.getValueType(); ParseUtils.requireAttributes(reader, keyAttributeName); String key = reader.getAttributeValue(null, keyAttributeName); ModelNode op = operation.get(attribute.getName(), key); parseEmbeddedElement(objectType, reader, op, keyAttributeName); ParseUtils.requireNoContent(reader); } } static class ObjectParser extends AttributeParser { @Override public boolean isParseAsElement() { return true; } @Override public void parseElement(AttributeDefinition attribute, XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException { assert attribute instanceof ObjectTypeAttributeDefinition; if (attribute.getXmlName().equals(reader.getLocalName())) { ObjectTypeAttributeDefinition objectType = ((ObjectTypeAttributeDefinition) attribute); ModelNode op = operation.get(attribute.getName()); op.setEmptyObject(); parseEmbeddedElement(objectType, reader, op); } else { throw ParseUtils.unexpectedElement(reader, Collections.singleton(attribute.getXmlName())); } ParseUtils.requireNoContent(reader); } static void parseEmbeddedElement(ObjectTypeAttributeDefinition attribute, XMLExtendedStreamReader reader, ModelNode op, String... additionalExpectedAttributes) throws XMLStreamException { AttributeDefinition[] valueTypes = attribute.getValueTypes(); Map<String, AttributeDefinition> attributes = Arrays.asList(valueTypes).stream() .collect(Collectors.toMap(AttributeDefinition::getXmlName, Function.identity())); Map<String, AttributeDefinition> attributeElements = Arrays.asList(valueTypes).stream() .filter(attributeDefinition -> attributeDefinition.getParser().isParseAsElement()) .collect(Collectors.toMap(a -> a.getParser().getXmlName(a) , Function.identity())); for (int i = 0; i < reader.getAttributeCount(); i++) { String attributeName = reader.getAttributeLocalName(i); String value = reader.getAttributeValue(i); if (attributes.containsKey(attributeName)) { AttributeDefinition def = attributes.get(attributeName); AttributeParser parser = def.getParser(); assert parser != null; parser.parseAndSetParameter(def, value, op, reader); } else if (Arrays.binarySearch(additionalExpectedAttributes, attributeName) < 0) { throw ParseUtils.unexpectedAttribute(reader, i, attributes.keySet()); } } // Check if there are also element attributes inside a group if (!attributeElements.isEmpty()) { while (reader.hasNext() && reader.nextTag() != END_ELEMENT) { String attrName = reader.getLocalName(); if (attributeElements.containsKey(attrName)) { AttributeDefinition ad = attributeElements.get(reader.getLocalName()); ad.getParser().parseElement(ad, reader, op); } else { throw ParseUtils.unexpectedElement(reader); } } } } } class WrappedObjectListParser extends AttributeParser { @Override public boolean isParseAsElement() { return true; } ObjectTypeAttributeDefinition getObjectType(AttributeDefinition attribute) { assert attribute instanceof ObjectListAttributeDefinition; ObjectListAttributeDefinition list = ((ObjectListAttributeDefinition) attribute); return list.getValueType(); } @Override public void parseElement(AttributeDefinition attribute, XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException { assert attribute instanceof ObjectListAttributeDefinition; ObjectListAttributeDefinition list = ((ObjectListAttributeDefinition) attribute); ObjectTypeAttributeDefinition objectType = list.getValueType(); ModelNode listValue = new ModelNode(); listValue.setEmptyList(); while (reader.hasNext() && reader.nextTag() != END_ELEMENT) { if (objectType.getXmlName().equals(reader.getLocalName())) { ModelNode op = listValue.add(); parseEmbeddedElement(objectType, reader, op); } else { throw ParseUtils.unexpectedElement(reader, Collections.singleton(objectType.getXmlName())); } if (!reader.isEndElement()) { ParseUtils.requireNoContent(reader); } } operation.get(attribute.getName()).set(listValue); } } class UnWrappedObjectListParser extends WrappedObjectListParser { @Override public String getXmlName(AttributeDefinition attribute) { return getObjectType(attribute).getXmlName(); } @Override public void parseElement(AttributeDefinition attribute, XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException { ObjectTypeAttributeDefinition objectType = getObjectType(attribute); ModelNode listValue = operation.get(attribute.getName()); if (!listValue.isDefined()){ listValue.setEmptyList(); } String xmlName = objectType.getXmlName(); if (xmlName.equals(reader.getLocalName())) { ModelNode op = listValue.add(); parseEmbeddedElement(objectType, reader, op); } else { throw ParseUtils.unexpectedElement(reader, Collections.singleton(xmlName)); } if (!reader.isEndElement()) { ParseUtils.requireNoContent(reader); } } } AttributeParser PROPERTIES_WRAPPED = new PropertiesParser(); AttributeParser PROPERTIES_UNWRAPPED = new PropertiesParser(false); AttributeParser OBJECT_MAP_WRAPPED = new ObjectMapParser(); AttributeParser OBJECT_MAP_UNWRAPPED = new ObjectMapParser(false); AttributeParser WRAPPED_OBJECT_LIST_PARSER = new WrappedObjectListParser(); AttributeParser UNWRAPPED_OBJECT_LIST_PARSER = new UnWrappedObjectListParser(); static AttributeParser getObjectMapAttributeParser(String keyElementName) { return new ObjectMapParser(null, null, true, keyElementName); } static AttributeParser getObjectMapAttributeParser(String elementName, String keyElementName, boolean wrapElement) { return new ObjectMapParser(null, elementName, wrapElement, keyElementName); } static AttributeParser getObjectMapAttributeParser(String elementName, boolean wrapElement) { return new ObjectMapParser(null, elementName, wrapElement, null); } static AttributeParser getObjectMapAttributeParser(String wrapperElementName, boolean wrapElement, String elementName, String keyElementName) { return new ObjectMapParser(wrapperElementName, elementName, wrapElement, keyElementName); } }