Skip to content

Commit

Permalink
Fixing schema problems with reports by introducing t:XmlAsStringType.
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Apr 30, 2014
1 parent a5bf0aa commit 17b378a
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 90 deletions.
Expand Up @@ -15,65 +15,53 @@
*/
package com.evolveum.midpoint.prism.parser;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;

import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Element;

import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.Objectable;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.Revivable;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.parser.util.XNodeProcessorUtil;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;
import com.evolveum.midpoint.prism.xjc.AnyArrayList;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.prism.xml.XsdTypeMapper;
import com.evolveum.midpoint.prism.xnode.ListXNode;
import com.evolveum.midpoint.prism.xnode.MapXNode;
import com.evolveum.midpoint.prism.xnode.PrimitiveXNode;
import com.evolveum.midpoint.prism.xnode.RootXNode;
import com.evolveum.midpoint.prism.xnode.XNode;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.Handler;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.exception.TunnelException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.prism.xml.ns._public.query_2.SearchFilterType;
import com.evolveum.prism.xml.ns._public.types_2.ItemPathType;
import com.evolveum.prism.xml.ns._public.types_2.ObjectDeltaType;
import com.evolveum.prism.xml.ns._public.types_2.ProtectedByteArrayType;
import com.evolveum.prism.xml.ns._public.types_2.ProtectedDataType;
import com.evolveum.prism.xml.ns._public.types_2.ProtectedStringType;
import com.evolveum.prism.xml.ns._public.types_2.RawType;
import com.evolveum.prism.xml.ns._public.types_2.XmlAsStringType;
import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Element;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class PrismBeanConverter {

Expand Down Expand Up @@ -122,6 +110,21 @@ public <T> T unmarshall(MapXNode xnode, Class<T> beanClass) throws SchemaExcepti
if (prismContext.getSchemaRegistry().determineDefinitionFromClass(beanClass) != null) {
return (T) prismContext.getXnodeProcessor().parseObject(xnode).asObjectable();
}
if (XmlAsStringType.class.equals(beanClass)) {
// reading a string represented a XML-style content
// used e.g. when reading report templates (embedded XML)
// A necessary condition: there may be only one map entry.
if (xnode.size() > 1) {
throw new SchemaException("Map with more than one item cannot be parsed as a string: " + xnode);
} else if (xnode.isEmpty()) {
return (T) new XmlAsStringType();
} else {
Map.Entry<QName,XNode> entry = xnode.entrySet().iterator().next();
DomParser domParser = prismContext.getParserDom();
String value = domParser.serializeToString(entry.getValue(), entry.getKey());
return (T) new XmlAsStringType(value);
}
}
T bean;
Set<String> keysToParse; // only these keys will be parsed (null if all)
if (SearchFilterType.class.isAssignableFrom(beanClass)) {
Expand Down Expand Up @@ -480,6 +483,9 @@ public <T> T unmarshallPrimitive(PrimitiveXNode<?> xprim, QName typeQName) throw
}

private <T> T unmarshallPrimitive(PrimitiveXNode<?> xprim, Class<T> classType) throws SchemaException {
if (XmlAsStringType.class.equals(classType)) {
return (T) new XmlAsStringType((String) xprim.getParsedValue(DOMUtil.XSD_STRING));
}
if (XmlTypeConverter.canConvert(classType)) {
// Trivial case, direct conversion
QName xsdType = XsdTypeMapper.toXsdType(classType);
Expand Down Expand Up @@ -589,7 +595,9 @@ public <T> XNode marshall(T bean) throws SchemaException {
return marshalSearchFilterType((SearchFilterType) bean);
} else if (bean instanceof RawType) {
return marshalRawValue((RawType) bean);
}
} else if (bean instanceof XmlAsStringType) {
return marshalXmlAsStringType((XmlAsStringType) bean);
}
else if (prismContext != null && prismContext.getSchemaRegistry().determineDefinitionFromClass(bean.getClass()) != null){
return prismContext.getXnodeProcessor().serializeObject(((Objectable)bean).asPrismObject()).getSubnode();
}
Expand Down Expand Up @@ -699,8 +707,15 @@ else if (prismContext != null && prismContext.getSchemaRegistry().determineDefin

return xmap;
}

public void revive(Object bean, final PrismContext prismContext) throws SchemaException {

private XNode marshalXmlAsStringType(XmlAsStringType bean) {
PrimitiveXNode xprim = new PrimitiveXNode<>();
xprim.setValue(bean.getContentAsString());
xprim.setTypeQName(DOMUtil.XSD_STRING);
return xprim;
}

public void revive(Object bean, final PrismContext prismContext) throws SchemaException {
Handler<Object> visitor = new Handler<Object>() {
@Override
public boolean handle(Object o) {
Expand Down
Expand Up @@ -471,22 +471,6 @@ private <T> T parsePrismPropertyRealValueFromMap(MapXNode xmap, QName typeName,
return (T) schemaDefType;
} else if (prismContext.getBeanConverter().canProcess(typeName)) {
return prismContext.getBeanConverter().unmarshall(xmap, typeName);
} else if (DOMUtil.XSD_STRING.equals(typeName)) {
// a bit of hack: trying to get a string but a Map is present
// e.g. when reading report templates (embedded XML)
// A necessary condition: there may be only one map entry.
if (xmap.size() > 1) {
throw new SchemaException("Map with more than one item cannot be parsed as a string. "
+ (propertyDefinition!=null ? ("Property definition: " + propertyDefinition.getName() + ". ") : "")
+ "Map: " + xmap);
} else if (xmap.isEmpty()) {
return (T) "";
} else {
Map.Entry<QName,XNode> entry = xmap.entrySet().iterator().next();
DomParser domParser = prismContext.getParserDom();
String value = domParser.serializeToString(entry.getValue(), entry.getKey());
return (T) value;
}
} else {
if (propertyDefinition != null) {
if (propertyDefinition.isRuntimeSchema()) {
Expand Down
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2010-2014 Evolveum
*
* 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.evolveum.prism.xml.ns._public.types_2;

import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.xml.DomAwareEqualsStrategy;
import com.evolveum.midpoint.util.xml.DomAwareHashCodeStrategy;
import org.jvnet.jaxb2_commons.lang.Equals;
import org.jvnet.jaxb2_commons.lang.EqualsStrategy;
import org.jvnet.jaxb2_commons.lang.HashCode;
import org.jvnet.jaxb2_commons.lang.HashCodeStrategy;
import org.jvnet.jaxb2_commons.locator.ObjectLocator;
import org.jvnet.jaxb2_commons.locator.util.LocatorUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlMixed;
import javax.xml.bind.annotation.XmlType;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
* A class used to hold string represented either as plain string or as XML markup. (Useful e.g. for jasper templates.)
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "XmlAsStringType", propOrder = {
"content"
})
public class XmlAsStringType implements Serializable, Cloneable, Equals, HashCode {

private final static long serialVersionUID = 201105211233L;
@XmlMixed
@XmlAnyElement(lax = false) // JAXB should not try to unmarshal inner elements
protected List<Object> content;

/**
* Creates a new {@code XmlAsStringType} instance.
*
*/
public XmlAsStringType() {
}

public XmlAsStringType(String value) {
content = new ArrayList<>();
content.add(value);
}

public List<Object> getContent() {
if (content == null) {
content = new ArrayList<>();
}
return this.content;
}

public String getContentAsString() {
StringBuilder sb = new StringBuilder();
for (Object object : getContent()) {
if (object instanceof String) {
sb.append(object);
} else if (object instanceof Node) {
sb.append(DOMUtil.serializeDOMToString((Node) object));
} else {
throw new IllegalStateException("Unexpected content in XmlAsStringType: " + (object!=null?object.getClass():"(null)"));
}
}
return sb.toString();
}

public int hashCode(ObjectLocator locator, HashCodeStrategy strategy) {
int currentHashCode = 1;
{
List<Object> theContent;
theContent = (((this.content!= null)&&(!this.content.isEmpty()))?this.getContent():null);
currentHashCode = strategy.hashCode(LocatorUtils.property(locator, "content", theContent), currentHashCode, theContent);
}
return currentHashCode;
}

public int hashCode() {
final HashCodeStrategy strategy = DomAwareHashCodeStrategy.INSTANCE;
return this.hashCode(null, strategy);
}

public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, Object object, EqualsStrategy strategy) {
if (!(object instanceof XmlAsStringType)) {
return false;
}
if (this == object) {
return true;
}
final XmlAsStringType that = ((XmlAsStringType) object);
{
List<Object> lhsContent;
lhsContent = (((this.content!= null)&&(!this.content.isEmpty()))?this.getContent():null);
List<Object> rhsContent;
rhsContent = (((that.content!= null)&&(!that.content.isEmpty()))?that.getContent():null);
if (!strategy.equals(LocatorUtils.property(thisLocator, "content", lhsContent), LocatorUtils.property(thatLocator, "content", rhsContent), lhsContent, rhsContent)) {
return false;
}
}
return true;
}

public boolean equals(Object object) {
final EqualsStrategy strategy = DomAwareEqualsStrategy.INSTANCE;
return equals(null, null, object, strategy);
}

/**
* Creates and returns a deep copy of this object.
*
* @return
* A deep copy of this object.
*/
@Override
public XmlAsStringType clone() {
final XmlAsStringType clone;
try {
clone = ((XmlAsStringType) super.clone());
} catch (CloneNotSupportedException e) {
throw new IllegalStateException("Couldn't clone object's superclass", e);
}
if (this.content != null) {
clone.content = new ArrayList<>();
for (Object o : this.getContent()) {
if (o instanceof String) {
clone.content.add(o);
} else if (o instanceof Node) {
clone.content.add(((Node) o).cloneNode(true));
} else {
throw new IllegalStateException("XmlAsStringType.clone: unexpected item in content: " + (o!=null?o.getClass():"(null)"));
}
}
}
return clone;
}
}
13 changes: 12 additions & 1 deletion infra/prism/src/main/resources/xml/ns/public/types-2.xsd
Expand Up @@ -408,7 +408,18 @@
</xsd:sequence>
<xsd:anyAttribute processContents="lax"/>
</xsd:complexType>


<xsd:complexType name="XmlAsStringType" mixed="true">
<xsd:annotation>
<xsd:documentation>
Represents a string that may contain unescaped XML data.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
</xsd:sequence>
</xsd:complexType>

<!-- ########################################### -->
<!-- ## DELTAS ## -->
<!-- ########################################### -->
Expand Down

0 comments on commit 17b378a

Please sign in to comment.