Skip to content

Commit

Permalink
Merge pull request #908 from ctomc/parser
Browse files Browse the repository at this point in the history
Improve parser to support properly persiting / parsing of multi element attributes
  • Loading branch information
bstansberry committed Jul 23, 2015
2 parents 4c8ef23 + 5d8c46f commit 72cf1eb
Show file tree
Hide file tree
Showing 17 changed files with 1,110 additions and 153 deletions.
Expand Up @@ -23,7 +23,6 @@
package org.jboss.as.controller;

import java.util.Iterator;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

Expand Down Expand Up @@ -84,6 +83,15 @@ public boolean isMarshallableAsElement(){
return false;
}


public void marshall(final AttributeDefinition attribute,final ModelNode resourceModel, final boolean marshallDefault, final XMLStreamWriter writer) throws XMLStreamException {
if (isMarshallableAsElement()){
marshallAsElement(attribute,resourceModel, marshallDefault, writer);
}else{
marshallAsAttribute(attribute, resourceModel, marshallDefault, writer);
}
}

private static class ListMarshaller extends DefaultAttributeMarshaller {
private final char delimiter;

Expand Down Expand Up @@ -133,6 +141,38 @@ public void marshallAsElement(AttributeDefinition attribute, ModelNode resourceM
}
}


private static class ObjectListMarshaller extends AttributeMarshaller {
private ObjectListMarshaller() {

}

@Override
public boolean isMarshallableAsElement() {
return true;
}

@Override
public void marshallAsElement(AttributeDefinition attribute, ModelNode resourceModel, boolean marshallDefault, XMLStreamWriter writer) throws XMLStreamException {
assert attribute instanceof ObjectListAttributeDefinition;
ObjectListAttributeDefinition list = ((ObjectListAttributeDefinition) attribute);
ObjectTypeAttributeDefinition objectType = list.getValueType();
AttributeDefinition[] valueTypes = objectType.getValueTypes();
if (resourceModel.hasDefined(attribute.getName())) {
writer.writeStartElement(attribute.getXmlName());
for (ModelNode element: resourceModel.get(attribute.getName()).asList()) {
writer.writeStartElement(objectType.getXmlName());
for (AttributeDefinition valueType : valueTypes) {
valueType.getAttributeMarshaller().marshall(valueType, element, false, writer);
}
writer.writeEndElement();
}
writer.writeEndElement();
}
}
}


/**
* simple marshaller
*/
Expand All @@ -157,4 +197,7 @@ public void marshallAsElement(AttributeDefinition attribute, ModelNode resourceM
* Marshaller for ObjectTypeAttributeDefinition. The object and all its complex types descendants will get marshalled as elements whereas simple types will get marshalled as attributes.
*/
public static final AttributeMarshaller ATTRIBUTE_OBJECT = new ObjectMarshaller(false);


public static final AttributeMarshaller OBJECT_LIST_MARSHALLER = new ObjectListMarshaller();
}
@@ -0,0 +1,87 @@
/*
* 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 javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.dmr.ModelNode;

/**
* @author Tomaz Cerar (c) 2015 Red Hat Inc.
*/
public interface AttributeMarshallers {

static class PropertiesAttributeMarshaller extends AttributeMarshaller {
private final String wrapperElement;
private final String elementName;
private final boolean wrapElement;

public PropertiesAttributeMarshaller(String wrapperElement, String elementName, boolean wrapElement) {
this.wrapperElement = wrapperElement;
this.elementName = elementName == null ? ModelDescriptionConstants.PROPERTY : elementName;
this.wrapElement = wrapElement;
}

public PropertiesAttributeMarshaller(String wrapperElement, boolean wrapElement) {
this(wrapperElement, null, wrapElement);
}

public PropertiesAttributeMarshaller() {
this(null, null, true);
}

@Override
public boolean isMarshallable(AttributeDefinition attribute, ModelNode resourceModel, boolean marshallDefault) {
return resourceModel.isDefined() && resourceModel.hasDefined(attribute.getName());
}

@Override
public void marshallAsElement(AttributeDefinition attribute, ModelNode resourceModel, boolean marshallDefault, XMLStreamWriter writer) throws XMLStreamException {
if (!resourceModel.hasDefined(attribute.getName())) {
return;
}
resourceModel = resourceModel.get(attribute.getName());
if (wrapElement) {
writer.writeStartElement(wrapperElement == null ? attribute.getName() : wrapperElement);
}
for (ModelNode property : resourceModel.asList()) {
writer.writeEmptyElement(elementName);
writer.writeAttribute(org.jboss.as.controller.parsing.Attribute.NAME.getLocalName(), property.asProperty().getName());
writer.writeAttribute(org.jboss.as.controller.parsing.Attribute.VALUE.getLocalName(), property.asProperty().getValue().asString());
}
if (wrapElement) {
writer.writeEndElement();
}
}

@Override
public void marshallAsAttribute(AttributeDefinition attribute, ModelNode resourceModel, boolean marshallDefault, XMLStreamWriter writer) throws XMLStreamException {
marshallAsElement(attribute, resourceModel, marshallDefault, writer);
}

@Override
public boolean isMarshallableAsElement() {
return true;
}
}


}
Expand Up @@ -25,12 +25,19 @@
package org.jboss.as.controller;


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.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.jboss.as.controller.operations.validation.ParameterValidator;
import org.jboss.as.controller.parsing.ParseUtils;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLExtendedStreamReader;

/**
* @author Tomaz Cerar (c) 2014 Red Hat Inc.
Expand Down Expand Up @@ -101,6 +108,18 @@ private ModelNode parse(final AttributeDefinition attribute, final String value)
return node;
}

public boolean isParseAsElement() {
return false;
}

public void parseElement(final AttributeDefinition attribute, final XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException {

}

public String getXmlName(final AttributeDefinition attribute){
return attribute.getXmlName();
}

public static final AttributeParser SIMPLE = new AttributeParser() {
};

Expand Down Expand Up @@ -138,6 +157,52 @@ public void parseAndSetParameter(AttributeDefinition attribute, String value, Mo
}
};

public static final AttributeParser OBJECT_LIST_PARSER = new AttributeParser() {
@Override
public boolean isParseAsElement() {
return true;
}

@Override
public void parseElement(AttributeDefinition attribute, XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException {
assert attribute instanceof ObjectListAttributeDefinition;

ObjectListAttributeDefinition list = ((ObjectListAttributeDefinition) attribute);
ObjectTypeAttributeDefinition objectType = list.getValueType();
AttributeDefinition[] valueTypes = objectType.getValueTypes();

Map<String, AttributeDefinition> attributes = Arrays.asList(valueTypes).stream()
.collect(Collectors.toMap(AttributeDefinition::getXmlName,
Function.identity()));
while (reader.hasNext() && reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
if (objectType.getXmlName().equals(reader.getLocalName())){
ModelNode op = operation.get(attribute.getName()).add();
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 {
throw ParseUtils.unexpectedAttribute(reader, i, attributes.keySet());
}
}
}else{
throw ParseUtils.unexpectedElement(reader, Collections.singleton(objectType.getXmlName()));
}
ParseUtils.requireNoContent(reader);
}
}
};

public static final AttributeParser OBJECT_PARSER = new AttributeParser() {

};



public static final class DiscardOldDefaultValueParser extends AttributeParser{
private final String value;

Expand All @@ -153,4 +218,5 @@ public ModelNode parse(AttributeDefinition attribute, String value, XMLStreamRea
return new ModelNode();
}
}

}
109 changes: 109 additions & 0 deletions controller/src/main/java/org/jboss/as/controller/AttributeParsers.java
@@ -0,0 +1,109 @@
/*
* 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 org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROPERTY;

import java.util.Collections;
import javax.xml.stream.XMLStreamConstants;
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 {

public static class PropertiesParser extends AttributeParser {
private final String wrapperElement;
private final String elementName;
private final boolean wrapElement;


public PropertiesParser(String wrapperElement, String elementName, boolean wrapElement) {
this.wrapperElement = wrapperElement;
this.elementName = elementName == null?PROPERTY:elementName;
this.wrapElement = 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);
}

@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 {
assert attribute instanceof PropertiesAttributeDefinition;
PropertiesAttributeDefinition property = (PropertiesAttributeDefinition) attribute;
String wrapper = wrapperElement == null ? property.getName() : wrapperElement;

if (wrapElement) {
if (!reader.getLocalName().equals(wrapper)) {
throw ParseUtils.unexpectedElement(reader, Collections.singleton(wrapper));
} else {
reader.nextTag();
}
}
do {
if (elementName.equals(reader.getLocalName())) {
property.parse(reader, operation);
} else {
throw ParseUtils.unexpectedElement(reader, Collections.singleton(elementName));
}

} while (reader.hasNext() && reader.nextTag() != XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals(elementName));

if (wrapElement) {
if (!reader.getLocalName().equals(wrapperElement)) {
ParseUtils.requireNoContent(reader);
}
}
}
}


AttributeParser PROPERTIES_WRAPPED = new PropertiesParser();
AttributeParser PROPERTIES_UNWRAPPED = new PropertiesParser(false);


}
Expand Up @@ -66,7 +66,7 @@ public void marshallAsElement(final AttributeDefinition attribute, final ModelNo

@Override
public boolean isMarshallableAsElement() {
return true;
return false;
}

protected String asString(ModelNode value) {
Expand Down
Expand Up @@ -225,13 +225,19 @@ protected void addAllowedValuesToDescription(ModelNode result, ParameterValidato
//Don't add allowed values for object types, since they simply enumerate the fields given in the value type
}

ObjectTypeAttributeDefinition getValueType() {
return valueType;
}

public static final class Builder extends ListAttributeDefinition.Builder<Builder, ObjectListAttributeDefinition> {
private final ObjectTypeAttributeDefinition valueType;

public Builder(final String name, final ObjectTypeAttributeDefinition valueType) {
super(name);
this.valueType = valueType;
setElementValidator(valueType.getValidator());
setAttributeParser(AttributeParser.OBJECT_LIST_PARSER);
setAttributeMarshaller(AttributeMarshaller.OBJECT_LIST_MARSHALLER);
}

public static Builder of(final String name, final ObjectTypeAttributeDefinition valueType) {
Expand Down

0 comments on commit 72cf1eb

Please sign in to comment.