blob: acbffe06ebb86f4c1b9121e6479dca779b7086e6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.uima.cas.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;
import org.apache.uima.cas.Feature;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.TypeSystem;
/**
* Class comment for TypeSystemUtils.java goes here.
*
*
*/
public abstract class TypeSystemUtils {
// Return value constants for feature path checking on type system
public static enum PathValid {
NEVER, POSSIBLE, ALWAYS
}
static abstract class TypeSystemParse {
private ParsingError error = null;
protected TypeSystemParse() {
super();
}
boolean hasError() {
return (this.error != null);
}
/**
* Returns the error.
*
* @return ParsingError
*/
ParsingError getError() {
return this.error;
}
/**
* Sets the error.
*
* @param error
* The error to set
*/
void setError(ParsingError error) {
this.error = error;
}
}
static class NameSpaceParse extends TypeSystemParse {
private String name;
NameSpaceParse() {
super();
}
/**
* Returns the name.
*
* @return String
*/
String getName() {
return this.name;
}
/**
* Sets the name.
*
* @param name
* The name to set
*/
void setName(String name) {
this.name = name;
}
}
static class TypeParse extends TypeSystemParse {
private String name;
private NameSpaceParse nameSpace;
TypeParse() {
super();
}
TypeParse(String name) {
this();
this.name = name;
}
boolean isQualified() {
return (this.nameSpace != null);
}
String getName() {
return this.name;
}
NameSpaceParse getNameSpace() {
return this.nameSpace;
}
/**
* Sets the name.
*
* @param name
* The name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets the nameSpace.
*
* @param nameSpace
* The nameSpace to set
*/
public void setNameSpace(NameSpaceParse nameSpace) {
this.nameSpace = nameSpace;
}
}
static class FeatureParse extends TypeSystemParse {
private TypeParse type;
private String name;
/**
* Returns the name.
*
* @return String
*/
public String getName() {
return this.name;
}
/**
* Returns the type.
*
* @return Type
*/
public TypeParse getType() {
return this.type;
}
/**
* Sets the name.
*
* @param name
* The name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets the type.
*
* @param type
* The type to set
*/
public void setType(TypeParse type) {
this.type = type;
}
}
static class ParsingError {
private int errorCode;
private int errorPosition;
/**
* Returns the errorCode.
*
* @return int
*/
public int getErrorCode() {
return this.errorCode;
}
/**
* Returns the errorPosition.
*
* @return int
*/
public int getErrorPosition() {
return this.errorPosition;
}
/**
* Sets the errorCode.
*
* @param errorCode
* The errorCode to set
*/
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
/**
* Sets the errorPosition.
*
* @param errorPosition
* The errorPosition to set
*/
public void setErrorPosition(int errorPosition) {
this.errorPosition = errorPosition;
}
}
public static boolean isIdentifier(String s) {
if (s == null) {
return false;
}
final int len = s.length();
if (s == null || len == 0) {
return false;
}
int pos = 0;
// Check that the first character is a letter.
if (!isIdentifierStart(s.charAt(pos))) {
return false;
}
++pos;
while (pos < len) {
if (!isIdentifierChar(s.charAt(pos))) {
return false;
}
++pos;
}
return true;
}
static boolean isNonQualifiedName(String s) {
return isIdentifier(s);
}
static boolean isIdentifierStart(char c) {
return Character.isLetter(c);
}
static boolean isIdentifierChar(char c) {
return (Character.isLetter(c) || Character.isDigit(c) || (c == '_'));
}
static private final String NAMESPACE_SEPARATOR_AS_STRING = "" + TypeSystem.NAMESPACE_SEPARATOR;
/**
* Check if <code>name</code> is a possible type name. Does not check if this type actually
* exists!
*
* @param name
* The name to check.
* @return <code>true</code> iff <code>name</code> is a possible type name.
*/
static boolean isTypeName(String name) {
// Create a string tokenizer that will split the string at the name
// space
// boundaries. We need to see the delimiters to make sure there are no
// gratuitous delimiters at the beginning or the end.
StringTokenizer tok = new StringTokenizer(name, NAMESPACE_SEPARATOR_AS_STRING, true);
// Loop over the tokens and check that every item is an identifier.
while (tok.hasMoreTokens()) {
// Any subsequence must start with an identifier.
if (!isIdentifier(tok.nextToken())) {
return false;
}
// If there is a next token, it must be a separator.
if (tok.hasMoreTokens()) {
if (!tok.nextToken().equals(NAMESPACE_SEPARATOR_AS_STRING)) {
return false;
}
// A sequence can not end in a separator.
if (!tok.hasMoreTokens()) {
return false;
}
}
}
return true;
}
static boolean isTypeNameSpaceName(String name) {
// Syntactically, there is no difference between a type name and a name
// space name.
return isTypeName(name);
}
/**
* Checks if a feature path is valid for a given type.
*
* <p>
* We distinguish three cases:
* <ol>
* <li><code>PathValid.NEVER</code>: there is no object of <code>type</code> on which
* <code>path</code> can ever be defined.</li>
* <li><code>PathValid.ALWAYS</code>: if all intermediate objects are non-null, this
* <code>path</code> will always be defined on any object of <code>type</code>. </li>
* <li><code>PathValid.POSSIBLE: some objects of <code>type</code> will have<code>path</code>
* defined, while others may not.</li>
* </ol>
* <b>Note:</b> we always assume that all references are not null. A return value of ALWAYS
* can of course not guarantee that all intermediate objects will always exist; only that if they
* exist, the path will be defined.
*
* @param type The type.
* @param path The path to check.
* @return One of {@link PathValid#ALWAYS ALWAYS}, {@link PathValid#POSSIBLE POSSIBLE}, or
* {@link PathValid#NEVER NEVER}.
*/
public static final PathValid isPathValid(Type type, List<String> path) {
Stack<String> fStack = new Stack<String>();
// Note: addAll() adds elements to the stack in the wrong order.
for (int i = (path.size() - 1); i >= 0; i--) {
fStack.push(path.get(i));
}
return isPathValid(type, fStack, PathValid.ALWAYS);
}
private static final PathValid isPathValid(Type type, Stack<String> path,
PathValid status) {
// If the path is empty, return the input status.
if (path.isEmpty()) {
return status;
}
// Pop the next feature name from the stack and check if it's defined for the current type.
String fName = path.pop();
Feature feat = type.getFeatureByBaseName(fName);
if (feat != null) {
// If feature is defined, we can continue directly.
return isPathValid(feat.getRange(), path, status);
}
// If feature is not defined for type, check to see if there are any subtypes for which the
// path is defined (possible).
List<Type> subtypes = new ArrayList<Type>();
getFeatureDefiningSubtypes(type, fName, subtypes);
for (int i = 0; i < subtypes.size(); i++) {
// Retrieve the feature value type
Type nextType = subtypes.get(i).getFeatureByBaseName(fName).getRange();
// Call isPathValid() on next type in chain.
PathValid newStatus = isPathValid(nextType, path, PathValid.POSSIBLE);
if (newStatus == PathValid.POSSIBLE) {
// If we found one, we can stop here and return.
return PathValid.POSSIBLE;
}
}
// No subtype was found for which the path was defined.
return PathValid.NEVER;
}
// Find subtypes that define the feature. Add subtypes to list.
private static final void getFeatureDefiningSubtypes(Type type, String fName, List<Type> types) {
TypeSystem ts = ((TypeImpl) type).getTypeSystem();
List<?> subtypes = ts.getDirectSubtypes(type);
for (int i = 0; i < subtypes.size(); i++) {
Type subtype = (Type) subtypes.get(i);
if (subtype.getFeatureByBaseName(fName) != null) {
types.add((Type) subtypes.get(i));
} else {
getFeatureDefiningSubtypes(subtype, fName, types);
}
}
}
/**
* Classify types into FS type, array type etc. For the full list of return types, see the
* <code>LowLevelCAS.TYPE_CLASS*</code> constants, as well as the documentation for
* {@link LowLevelCAS#ll_getTypeClass(int) LowLevelCAS.ll_getTypeClass(int)}.
*
* @param type
* The type to classify.
* @return An integer encoding the the type class. See above.
*/
public static final int classifyType(Type type) {
LowLevelTypeSystem llts = ((TypeImpl) type).getTypeSystem().getLowLevelTypeSystem();
return llts.ll_getTypeClass(llts.ll_getCodeForType(type));
}
}