/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.wadl.inference.support;
import org.apache.xmlbeans.XmlAnySimpleType;
import org.apache.xmlbeans.XmlBase64Binary;
import org.apache.xmlbeans.XmlBoolean;
import org.apache.xmlbeans.XmlDate;
import org.apache.xmlbeans.XmlDateTime;
import org.apache.xmlbeans.XmlDecimal;
import org.apache.xmlbeans.XmlGDay;
import org.apache.xmlbeans.XmlGMonth;
import org.apache.xmlbeans.XmlGYear;
import org.apache.xmlbeans.XmlGYearMonth;
import org.apache.xmlbeans.XmlHexBinary;
import org.apache.xmlbeans.XmlInteger;
import org.apache.xmlbeans.XmlNegativeInteger;
import org.apache.xmlbeans.XmlNonNegativeInteger;
import org.apache.xmlbeans.XmlNonPositiveInteger;
import org.apache.xmlbeans.XmlPositiveInteger;
import org.apache.xmlbeans.XmlString;
import org.apache.xmlbeans.XmlTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* XML Schema inferred from gathered XML data.
*
* @author Dain Nilsson
*/
public class TypeInferrer {
private static TypeInferrer ref;
private TypeTree types;
private Map<XmlAnySimpleType, TypeTree> typeTable;
/**
* Get the instance of the XmlAnySimpleType with the type xs:<typeName>.
*
* @param typeName
* @return Returns the XmlAnySimpleType, if available. Otherwise returns
* null.
*/
public static XmlAnySimpleType getType(String typeName) {
for (XmlAnySimpleType item : getRef().typeTable.keySet()) {
if (item.schemaType().getName().getLocalPart().equals(typeName)) {
return item;
}
}
return null;
}
/**
* Get the XmlAnySimpleType that describes a simple value that is empty.
*
* @return Returns the xs:string XmlAnySimpleType.
*/
public static XmlAnySimpleType getBlankType() {
return getRef().types.type;
}
/**
* Given a value and a type, a new type will be returned that validates
* values for both the given type, and the new value.
*
* @param value The new value to expand the type for.
* @param baseType The type to be expanded.
* @return The new expanded type.
*/
public static XmlAnySimpleType expandTypeForValue(String value, XmlAnySimpleType baseType) {
return getRef().expandTypeForValueInternal(baseType, value);
}
/**
* Given a simple value, infers the type of the value.
*
* @param value The value to assign a type to.
* @return Returns the inferred type for the given value.
*/
public static XmlAnySimpleType inferSimpleType(String value) {
return getRef().inferSimpleTypeRec(value, getRef().types);
}
/**
* Validates a string against an XmlAnySimpleType.
*
* @param value The value to validate.
* @param type The XmlAnySimpleType to validate against.
* @return True if the value validates, false if not.
*/
public static boolean validateSimpleType(String value, XmlAnySimpleType type) {
try {
type.setStringValue(value);
return type.validate();
} catch (Exception e) {
return false;
}
}
private static TypeInferrer getRef() {
if (ref == null) {
ref = new TypeInferrer();
}
return ref;
}
private TypeInferrer() {
typeTable = new HashMap<XmlAnySimpleType, TypeTree>();
TypeTree xmlbool = new TypeTree(XmlBoolean.Factory.newInstance());
typeTable.put(xmlbool.type, xmlbool);
TypeTree xmlbool2 = new TypeTree(XmlBoolean.Factory.newInstance());
typeTable.put(xmlbool2.type, xmlbool2);
TypeTree xmlnegint = new TypeTree(XmlNegativeInteger.Factory.newInstance());
typeTable.put(xmlnegint.type, xmlnegint);
TypeTree xmlposint = new TypeTree(XmlPositiveInteger.Factory.newInstance());
typeTable.put(xmlposint.type, xmlposint);
TypeTree xmlnonnegint = new TypeTree(XmlNonNegativeInteger.Factory.newInstance());
typeTable.put(xmlnonnegint.type, xmlnonnegint);
xmlnonnegint.addChild(xmlposint);
xmlnonnegint.addChild(xmlbool);
TypeTree xmlnonposint = new TypeTree(XmlNonPositiveInteger.Factory.newInstance());
typeTable.put(xmlnonposint.type, xmlnonposint);
xmlnonposint.addChild(xmlnegint);
TypeTree xmlint = new TypeTree(XmlInteger.Factory.newInstance());
typeTable.put(xmlint.type, xmlint);
xmlint.addChild(xmlnonnegint);
xmlint.addChild(xmlnonposint);
TypeTree xmldec = new TypeTree(XmlDecimal.Factory.newInstance());
typeTable.put(xmldec.type, xmldec);
xmldec.addChild(xmlint);
TypeTree xmldate = new TypeTree(XmlDate.Factory.newInstance());
typeTable.put(xmldate.type, xmldate);
TypeTree xmltime = new TypeTree(XmlTime.Factory.newInstance());
typeTable.put(xmltime.type, xmltime);
TypeTree xmldatetime = new TypeTree(XmlDateTime.Factory.newInstance());
typeTable.put(xmldatetime.type, xmldatetime);
TypeTree xmlhexbin = new TypeTree(XmlHexBinary.Factory.newInstance());
typeTable.put(xmlhexbin.type, xmlhexbin);
TypeTree xmlb64bin = new TypeTree(XmlBase64Binary.Factory.newInstance());
typeTable.put(xmlb64bin.type, xmlb64bin);
TypeTree xmlgyearmonth = new TypeTree(XmlGYearMonth.Factory.newInstance());
typeTable.put(xmlgyearmonth.type, xmlgyearmonth);
TypeTree xmlgyear = new TypeTree(XmlGYear.Factory.newInstance());
typeTable.put(xmlgyear.type, xmlgyear);
TypeTree xmlgmonth = new TypeTree(XmlGMonth.Factory.newInstance());
typeTable.put(xmlgmonth.type, xmlgmonth);
TypeTree xmlgday = new TypeTree(XmlGDay.Factory.newInstance());
typeTable.put(xmlgday.type, xmlgday);
TypeTree xmlstring = new TypeTree(XmlString.Factory.newInstance());
typeTable.put(xmlstring.type, xmlstring);
xmlstring.addChild(xmldec);
xmlstring.addChild(xmldate);
xmlstring.addChild(xmltime);
xmlstring.addChild(xmldatetime);
xmlstring.addChild(xmlbool2);
xmlstring.addChild(xmlgyearmonth);
xmlstring.addChild(xmlgyear);
xmlstring.addChild(xmlgmonth);
xmlstring.addChild(xmlgday);
xmlstring.addChild(xmlhexbin);
// xmlstring.addChild(xmlb64bin);
types = xmlstring;
}
private XmlAnySimpleType expandTypeForValueInternal(XmlAnySimpleType type, String value) {
TypeTree p = typeTable.get(type);
while (!validateSimpleType(value, p.type)) {
p = p.parent;
}
return p.type;
}
private XmlAnySimpleType inferSimpleTypeRec(String value, TypeTree p) {
for (TypeTree item : p.children) {
if (validateSimpleType(value, item.type)) {
return inferSimpleTypeRec(value, item);
}
}
return p.type;
}
private class TypeTree {
public XmlAnySimpleType type;
public TypeTree parent;
public List<TypeTree> children;
public TypeTree(XmlAnySimpleType type) {
this.type = type;
children = new ArrayList<TypeTree>();
}
public void addChild(TypeTree type) {
children.add(type);
type.parent = this;
}
}
}