package com.evolveum.prism.xml.ns._public.types_3; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.path.ItemPath; 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.exception.SchemaException; import com.evolveum.midpoint.util.exception.SystemException; import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jvnet.jaxb2_commons.lang.Equals; import org.jvnet.jaxb2_commons.lang.EqualsStrategy; import org.jvnet.jaxb2_commons.locator.ObjectLocator; import javax.xml.namespace.QName; import java.io.Serializable; import java.util.Objects; /** * A class used to hold raw XNodes until the definition for such an object is known. */ public class RawType implements Serializable, Cloneable, Equals, Revivable { private static final long serialVersionUID = 4430291958902286779L; /** * This is obligatory. */ private transient PrismContext prismContext; /* * At most one of these two values (xnode, parsed) should be set. */ /** * Unparsed value. It is set on RawType instance construction. */ private XNode xnode; /** * Parsed value. It is computed when calling getParsedValue/getParsedItem methods. * * Beware: At most one of these fields (xnode, parsed) may be non-null at any instant. */ private PrismValue parsed; public RawType(PrismContext prismContext) { Validate.notNull(prismContext, "prismContext is not set - perhaps a forgotten call to adopt() somewhere?"); this.prismContext = prismContext; } public RawType(XNode xnode, @NotNull PrismContext prismContext) { this(prismContext); this.xnode = xnode; } public RawType(PrismValue parsed, @NotNull PrismContext prismContext) { this.prismContext = prismContext; this.parsed = parsed; } @Override public void revive(PrismContext prismContext) throws SchemaException { Validate.notNull(prismContext); this.prismContext = prismContext; if (parsed != null) { parsed.revive(prismContext); } } //region General getters/setters public XNode getXnode() { return xnode; } @NotNull public RootXNode getRootXNode(@NotNull QName itemName) { return new RootXNode(itemName, xnode); } public PrismContext getPrismContext() { return prismContext; } //endregion //region Parsing and serialization // itemDefinition may be null; in that case we do the best what we can public <IV extends PrismValue,ID extends ItemDefinition> IV getParsedValue(@Nullable ItemDefinition itemDefinition, @Nullable QName itemName) throws SchemaException { if (parsed != null) { return (IV) parsed; } else if (xnode != null) { IV value; if (itemDefinition != null && !(itemDefinition instanceof PrismPropertyDefinition && ((PrismPropertyDefinition) itemDefinition).isAnyType())) { if (itemName == null) { itemName = itemDefinition.getName(); } checkPrismContext(); Item<IV,ID> subItem = prismContext.parserFor(getRootXNode(itemName)).name(itemName).definition(itemDefinition).parseItem(); if (!subItem.isEmpty()){ value = subItem.getValue(0); } else { value = null; } xnode = null; parsed = value; return (IV) parsed; } else { // we don't really want to set 'parsed', as we didn't performed real parsing return (IV) PrismPropertyValue.createRaw(xnode); } } else { return null; } } public <V,ID extends ItemDefinition> V getParsedRealValue(ID itemDefinition, ItemPath itemPath) throws SchemaException { if (parsed == null && xnode != null) { if (itemDefinition == null) { return prismContext.parserFor(xnode.toRootXNode()).parseRealValue(); // TODO what will be the result without definition? } else { QName itemName = ItemPath.getName(itemPath.lastNamed()); getParsedValue(itemDefinition, itemName); } } if (parsed != null) { return parsed.getRealValue(); } return null; } public <T> T getParsedRealValue(@NotNull Class<T> clazz) throws SchemaException { if (parsed != null) { if (clazz.isAssignableFrom(parsed.getRealValue().getClass())) { return (T) parsed.getRealValue(); } else { throw new IllegalArgumentException("Parsed value ("+parsed.getClass()+") is not assignable to "+clazz); } } else if (xnode != null) { return (T) prismContext.parserFor(xnode.toRootXNode()).parseRealValue(clazz); } else { return null; } } public <IV extends PrismValue,ID extends ItemDefinition> Item<IV,ID> getParsedItem(ID itemDefinition) throws SchemaException { Validate.notNull(itemDefinition); return getParsedItem(itemDefinition, itemDefinition.getName()); } public <IV extends PrismValue,ID extends ItemDefinition> Item<IV,ID> getParsedItem(ID itemDefinition, QName itemName) throws SchemaException { Validate.notNull(itemDefinition); Validate.notNull(itemName); Item<IV,ID> item = itemDefinition.instantiate(); IV newValue = getParsedValue(itemDefinition, itemName); if (newValue != null) { item.add((IV) newValue.clone()); } return item; } // // Returns either an item or a real value. // // VERY EXPERIMENTAL. // public Object getParsedItemOrRealValue() throws SchemaException { // if (parsed != null) { // return // } else if (xnode != null) { // return prismContext.parserFor(xnode.toRootXNode()).parseItemOrRealValue(); // } else { // return null; // } // } public XNode serializeToXNode() throws SchemaException { if (xnode != null) { // QName type = xnode.getTypeQName(); // if (xnode instanceof PrimitiveXNode && type != null){ // if (!((PrimitiveXNode)xnode).isParsed()){ // Object realValue = PrismUtil.getXnodeProcessor(prismContext).parseAnyValue(xnode, ParsingContext.createDefault()); // ((PrimitiveXNode)xnode).setValue(realValue, type); // } // } return xnode; } else if (parsed != null) { checkPrismContext(); return prismContext.xnodeSerializer().root(new QName("dummy")).serialize(parsed).getSubnode(); } else { return null; // or an exception here? } } //endregion //region Cloning, comparing, dumping (TODO) public RawType clone() { RawType clone = new RawType(prismContext); if (xnode != null) { clone.xnode = xnode.clone(); } else if (parsed != null) { clone.parsed = parsed.clone(); } return clone; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((xnode == null) ? 0 : xnode.hashCode()); result = prime * result + ((parsed == null) ? 0 : parsed.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; RawType other = (RawType) obj; if (xnode != null && other.xnode != null) { return xnode.equals(other.xnode); } else if (parsed != null && other.parsed != null) { return parsed.equals(other.parsed); } else { return xnodeSerializationsAreEqual(other); } } private boolean xnodeSerializationsAreEqual(RawType other) { try { return Objects.equals(serializeToXNode(), other.serializeToXNode()); } catch (SchemaException e) { // or should we silently return false? throw new SystemException("Couldn't serialize RawType to XNode when comparing them", e); } } @Override public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, Object that, EqualsStrategy equalsStrategy) { return equals(that); } //endregion private void checkPrismContext() { if (prismContext == null) { throw new IllegalStateException("prismContext is not set - perhaps a forgotten call to adopt() somewhere?"); } } public static RawType create(String value, PrismContext prismContext) { PrimitiveXNode<String> xnode = new PrimitiveXNode<>(value); RawType rv = new RawType(xnode, prismContext); return rv; } public static RawType create(XNode node, PrismContext prismContext) { return new RawType(node, prismContext); } }