blob: 1938b4009dc83e722348a464032ed3d2f1b312ad [file] [log] [blame]
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* 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.intellij.lang.xpath.psi;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.xml.namespace.QName;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/*
* Created by IntelliJ IDEA.
* User: sweinreuter
* Date: 07.01.11
*/
@SuppressWarnings({"UnusedDeclaration"})
public class XPath2Type extends XPathType {
public static final String XMLSCHEMA_NS = "http://www.w3.org/2001/XMLSchema";
private static Map<QName, XPath2Type> ourMap = new HashMap<QName, XPath2Type>();
public static final XPath2Type ITEM = createItemType("item()", ANY);
public static final XPath2Type NODE = createItemType("node()", ITEM);
public static final XPath2Type TEXT = createItemType("text()", ITEM);
public static final XPath2Type ELEMENT = createItemType("element()", NODE);
public static final XPath2Type ATTRIBUTE = createItemType("attribute()", NODE);
public static final XPath2Type PROCESSING_INSTRUCTION = createItemType("processing-instruction()", NODE);
public static final XPath2Type NODE_SEQUENCE = XPath2SequenceType.create(NODE, XPath2SequenceType.Cardinality.ZERO_OR_MORE);
public static final XPath2Type SEQUENCE = XPath2SequenceType.create(ITEM, XPath2SequenceType.Cardinality.ZERO_OR_MORE);
public static final XPath2Type ANYATOMICTYPE = createSchemaType("anyAtomicType", ITEM);
public static final XPath2Type UNTYPEDATOMIC = createSchemaType("untypedAtomic", ANYATOMICTYPE);
public static final XPath2Type STRING = createSchemaType("string", ANYATOMICTYPE);
public static final XPath2Type NORMALIZEDSTRING = createSchemaType("normalizedString", STRING);
public static final XPath2Type TOKEN = createSchemaType("token", NORMALIZEDSTRING);
public static final XPath2Type LANGUAGE = createSchemaType("language", TOKEN);
public static final XPath2Type NMTOKEN = createSchemaType("NMTOKEN", TOKEN);
public static final XPath2Type NAME = createSchemaType("Name", TOKEN);
public static final XPath2Type NCNAME = createSchemaType("NCName", NAME);
public static final XPath2Type ID = createSchemaType("ID", NCNAME);
public static final XPath2Type IDREF = createSchemaType("IDREF", NCNAME);
public static final XPath2Type ENTITY = createSchemaType("ENTITY", NCNAME);
public static final XPath2Type BOOLEAN = createSchemaType("boolean", ANYATOMICTYPE);
public static final XPath2Type BOOLEAN_STRICT = new SchemaType("boolean", BOOLEAN);
public static final XPath2Type NUMERIC = createSchemaType("numeric", ANYATOMICTYPE);
public static final XPath2Type FLOAT = createSchemaType("float", NUMERIC);
public static final XPath2Type DOUBLE = createSchemaType("double", NUMERIC);
public static final XPath2Type DECIMAL = createSchemaType("decimal", NUMERIC);
public static final XPath2Type INTEGER = createSchemaType("integer", DECIMAL);
public static final XPath2Type QNAME = createSchemaType("QName", ANYATOMICTYPE);
public static final XPath2Type ANYURI = createSchemaType("anyURI", ANYATOMICTYPE);
public static final XPath2Type BASE64BINARY = createSchemaType("base64Binary", ANYATOMICTYPE);
public static final XPath2Type HEXBINARY = createSchemaType("hexBinary", ANYATOMICTYPE);
public static final XPath2Type DATE = createSchemaType("date", ANYATOMICTYPE);
public static final XPath2Type TIME = createSchemaType("time", ANYATOMICTYPE);
public static final XPath2Type DATETIME = createSchemaType("dateTime", ANYATOMICTYPE);
public static final XPath2Type DURATION = createSchemaType("duration", ANYATOMICTYPE);
public static final XPath2Type DAYTIMEDURATION = createSchemaType("dayTimeDuration", DURATION);
public static final XPath2Type YEARMONTHDURATION = createSchemaType("yearMonthDuration", DURATION);
public static final XPath2Type GYEARMONTH = createSchemaType("gYearMonth", ANYATOMICTYPE);
public static final XPath2Type GYEAR = createSchemaType("gYear", ANYATOMICTYPE);
public static final XPath2Type GMONTHDAY = createSchemaType("gMonthDay", ANYATOMICTYPE);
public static final XPath2Type GDAY = createSchemaType("gDay", ANYATOMICTYPE);
public static final XPath2Type GMONTH = createSchemaType("gMonth", ANYATOMICTYPE);
private final XPathType mySuperType;
protected XPath2Type(String s, XPathType superType) {
super(s, false);
mySuperType = superType;
}
protected static XPath2Type createSchemaType(String s, XPathType superType) {
final XPath2Type type = new SchemaType(s, superType);
ourMap.put(new QName(XMLSCHEMA_NS, s), type);
return type;
}
protected static XPath2Type createItemType(String s, XPathType superType) {
final XPath2Type type = new ItemType(s, superType);
ourMap.put(new QName("", s), type);
return type;
}
public XPathType getSuperType() {
return mySuperType;
}
@Override
public boolean isAssignableFrom(@NotNull XPathType type) {
if (type instanceof XPath2SequenceType) {
type = ((XPath2SequenceType)type).getType();
}
if (type instanceof XPath2Type) {
if (this.equals(type)) {
return true;
} else {
XPathType t = ((XPath2Type)type).getSuperType();
while (t != null) {
if (t.equals(this)) return true;
if (!(t instanceof XPath2Type)) {
break;
}
t = ((XPath2Type)t).getSuperType();
}
}
}
return false;
}
@Override
public boolean canBePromotedTo(XPathType type) {
while (type instanceof XPath2SequenceType) {
type = ((XPath2SequenceType)type).getType();
}
if (this == ITEM || NODE.isAssignableFrom(this) || (this == ANYATOMICTYPE && ANYATOMICTYPE.isAssignableFrom(type))) return true;
if (FLOAT.isAssignableFrom(this) && type == DOUBLE) return true;
if (DECIMAL.isAssignableFrom(this) && (type == DOUBLE || type == FLOAT)) return true;
if (INTEGER.isAssignableFrom(this) && (type == DOUBLE || type == FLOAT || type == INTEGER || type == DECIMAL)) return true;
if (ANYURI.isAssignableFrom(this) && type == STRING) return true;
// effective boolean value
if (ANYURI.isAssignableFrom(this) && type == BOOLEAN) return true;
if (STRING.isAssignableFrom(this) && type == BOOLEAN) return true;
if (NUMERIC.isAssignableFrom(this) && type == BOOLEAN) return true;
if (this == UNTYPEDATOMIC && type == BOOLEAN) return true;
// function parameters do not use eff. boolean value, type must strictly match
if (this == BOOLEAN && type == BOOLEAN_STRICT) return true;
return false;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final XPath2Type type = (XPath2Type)o;
if (!mySuperType.equals(type.mySuperType)) return false;
if (!Comparing.equal(getQName(), type.getQName())) {
return false;
}
return true;
}
@Override
public int hashCode() {
return getName().hashCode() + mySuperType.hashCode() * 5;
}
@Nullable
public static XPath2Type schemaType(String name) {
return ourMap.get(new QName(XMLSCHEMA_NS, name));
}
@Nullable
public static XPath2Type fromName(QName name) {
final String local = name.getLocalPart();
if (local.endsWith("*")) {
return XPath2SequenceType.create(lookupSequenceType(name, local), XPath2SequenceType.Cardinality.ZERO_OR_MORE);
} else if (local.endsWith("+")) {
return XPath2SequenceType.create(lookupSequenceType(name, local), XPath2SequenceType.Cardinality.ONE_OR_MORE);
} else if (local.endsWith("?")) {
return XPath2SequenceType.create(lookupSequenceType(name, local), XPath2SequenceType.Cardinality.OPTIONAL);
}
return ourMap.get(name);
}
public QName getQName() {
return new QName(null, type);
}
private static XPathType lookupSequenceType(QName name, String local) {
final XPath2Type type = ourMap.get(new QName(name.getNamespaceURI(), local.substring(0, local.length() - 1)));
return type != null ? type : UNKNOWN;
}
public static XPathType mapType(XPathType type) {
if (type == XPathType.STRING) {
type = STRING;
} else if (type == XPathType.BOOLEAN) {
type = BOOLEAN;
} else if (type == XPathType.NUMBER) {
type = NUMERIC;
} else if (type == XPathType.NODESET) {
type = XPath2SequenceType.create(NODE, XPath2SequenceType.Cardinality.ZERO_OR_MORE);
}
return type;
}
public static class ItemType extends XPath2Type {
ItemType(String name, XPathType superType) {
super(name, superType);
}
@Override
public boolean isAssignableFrom(@NotNull XPathType type) {
return super.isAssignableFrom(type) || type == NODESET || this == ITEM;
}
}
public static class SchemaType extends XPath2Type {
SchemaType(String name, XPathType superType) {
super(name, superType);
}
@Override
public String getName() {
return "xs:" + super.getName();
}
@Override
public QName getQName() {
return new QName(XMLSCHEMA_NS, type);
}
@Override
public boolean isAbstract() {
return this == NUMERIC;
}
public static List<XPath2Type> listSchemaTypes() {
return ContainerUtil.filter(ourMap.values(), new Condition<XPath2Type>() {
@Override
public boolean value(XPath2Type type) {
return type.getQName().getNamespaceURI().equals(XMLSCHEMA_NS) && !type.isAbstract();
}
});
}
}
}