/************************************************************** * * 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 com.sun.star.uno; import java.util.HashMap; /** * Represents the UNO built-in type TYPE. * *

The UNO type is not directly mapped to java.lang.Class for at * least two reasons. For one, some UNO types (like UNSIGNED * SHORT) do not have a matching Java class. For another, it can be * necessary to describe a type which is unknown to the Java runtime system * (for example, for delaying the need of a class, so that it is possible to * generate it on the fly.) * *

A Type is uniquely determined by its type class (a * TypeClass) and its type name (a String); these two * will never be null. A Type may have an additional * "z class" (a java.lang.Class), giving a Java class type that * corresponds to the UNO type. Also, a Type can cache a type * description (a com.sun.star.uno.ITypeDescription), which can be * computed and set by TypeDescription.getTypeDescription. */ public class Type { // The following private static members and static initializer must come // first in the class definition, so that the class can be initialized // sucessfully: private static final String TYPE_NAME_VOID = "void"; private static final String TYPE_NAME_BOOLEAN = "boolean"; private static final String TYPE_NAME_BYTE = "byte"; private static final String TYPE_NAME_SHORT = "short"; private static final String TYPE_NAME_UNSIGNED_SHORT = "unsigned short"; private static final String TYPE_NAME_LONG = "long"; private static final String TYPE_NAME_UNSIGNED_LONG = "unsigned long"; private static final String TYPE_NAME_HYPER = "hyper"; private static final String TYPE_NAME_UNSIGNED_HYPER = "unsigned hyper"; private static final String TYPE_NAME_FLOAT = "float"; private static final String TYPE_NAME_DOUBLE = "double"; private static final String TYPE_NAME_CHAR = "char"; private static final String TYPE_NAME_STRING = "string"; private static final String TYPE_NAME_TYPE = "type"; private static final String TYPE_NAME_ANY = "any"; // must be sorted same as TypeClass: private static final String[] __typeClassToTypeName = new String[] { TYPE_NAME_VOID, TYPE_NAME_CHAR, TYPE_NAME_BOOLEAN, TYPE_NAME_BYTE, TYPE_NAME_SHORT, TYPE_NAME_UNSIGNED_SHORT, TYPE_NAME_LONG, TYPE_NAME_UNSIGNED_LONG, TYPE_NAME_HYPER, TYPE_NAME_UNSIGNED_HYPER, TYPE_NAME_FLOAT, TYPE_NAME_DOUBLE, TYPE_NAME_STRING, TYPE_NAME_TYPE, TYPE_NAME_ANY }; private static final HashMap __javaClassToTypeClass = new HashMap(); static { __javaClassToTypeClass.put( void.class, new TypeClass[] { TypeClass.VOID, TypeClass.VOID }); __javaClassToTypeClass.put( Void.class, new TypeClass[] { TypeClass.VOID, TypeClass.VOID }); __javaClassToTypeClass.put( boolean.class, new TypeClass[] { TypeClass.BOOLEAN, TypeClass.BOOLEAN }); __javaClassToTypeClass.put( Boolean.class, new TypeClass[] { TypeClass.BOOLEAN, TypeClass.BOOLEAN }); __javaClassToTypeClass.put( byte.class, new TypeClass[] { TypeClass.BYTE, TypeClass.BYTE }); __javaClassToTypeClass.put( Byte.class, new TypeClass[] { TypeClass.BYTE, TypeClass.BYTE }); __javaClassToTypeClass.put( short.class, new TypeClass[] { TypeClass.SHORT, TypeClass.UNSIGNED_SHORT }); __javaClassToTypeClass.put( Short.class, new TypeClass[] { TypeClass.SHORT, TypeClass.UNSIGNED_SHORT }); __javaClassToTypeClass.put( int.class, new TypeClass[] { TypeClass.LONG, TypeClass.UNSIGNED_LONG }); __javaClassToTypeClass.put( Integer.class, new TypeClass[] { TypeClass.LONG, TypeClass.UNSIGNED_LONG }); __javaClassToTypeClass.put( long.class, new TypeClass[] { TypeClass.HYPER, TypeClass.UNSIGNED_HYPER }); __javaClassToTypeClass.put( Long.class, new TypeClass[] { TypeClass.HYPER, TypeClass.UNSIGNED_HYPER }); __javaClassToTypeClass.put( float.class, new TypeClass[] { TypeClass.FLOAT, TypeClass.FLOAT }); __javaClassToTypeClass.put( Float.class, new TypeClass[] { TypeClass.FLOAT, TypeClass.FLOAT }); __javaClassToTypeClass.put( double.class, new TypeClass[] { TypeClass.DOUBLE, TypeClass.DOUBLE }); __javaClassToTypeClass.put( Double.class, new TypeClass[] { TypeClass.DOUBLE, TypeClass.DOUBLE }); __javaClassToTypeClass.put( char.class, new TypeClass[] { TypeClass.CHAR, TypeClass.CHAR }); __javaClassToTypeClass.put( Character.class, new TypeClass[] { TypeClass.CHAR, TypeClass.CHAR }); __javaClassToTypeClass.put( String.class, new TypeClass[] { TypeClass.STRING, TypeClass.STRING }); __javaClassToTypeClass.put( Type.class, new TypeClass[] { TypeClass.TYPE, TypeClass.TYPE }); __javaClassToTypeClass.put( Any.class, new TypeClass[] { TypeClass.ANY, TypeClass.ANY }); __javaClassToTypeClass.put( Object.class, new TypeClass[] { TypeClass.ANY, TypeClass.INTERFACE }); } public static final Type VOID = new Type(void.class); public static final Type CHAR = new Type(char.class); public static final Type BOOLEAN = new Type(boolean.class); public static final Type BYTE = new Type(byte.class); public static final Type SHORT = new Type(short.class); public static final Type UNSIGNED_SHORT = new Type( TYPE_NAME_UNSIGNED_SHORT, TypeClass.UNSIGNED_SHORT); public static final Type LONG = new Type(int.class); public static final Type UNSIGNED_LONG = new Type( TYPE_NAME_UNSIGNED_LONG, TypeClass.UNSIGNED_LONG); public static final Type HYPER = new Type(long.class); public static final Type UNSIGNED_HYPER = new Type( TYPE_NAME_UNSIGNED_HYPER, TypeClass.UNSIGNED_HYPER); public static final Type FLOAT = new Type(float.class); public static final Type DOUBLE = new Type(double.class); public static final Type STRING = new Type(String.class); public static final Type TYPE = new Type(Type.class); public static final Type ANY = new Type(Any.class); /** * Constructs a new Type which defaults to VOID. */ public Type() { init(null, void.class, false, false); } /** * Constructs a new Type with the given type class and type * name. * * @param typeName the type name. Must not be null. * @param typeClass the type class. Must not be null, and must * match the typeName (for example, it is illegal to * combine a typeName of "void" with a * typeClass of BOOLEAN). */ public Type(String typeName, TypeClass typeClass) { _typeClass = typeClass; _typeName = typeName; } /** * Constructs a new Type from the given * java.lang.Class. * *

This is equivalent to Type(zClass, false).

* * @param zClass the Java class of this type. Must not be * null. */ public Type(Class zClass) { init(null, zClass, false, false); } /** * Constructs a new Type from the given * java.lang.Class, handling ambiguous cases. * *

In certain cases, one Java class corresponds to two UNO types (e.g., * the Java class short[].class corresponds to both a sequence * of SHORT and a sequence of UNSIGNED SHORT in * UNO). In such ambiguous cases, the parameter alternative * controls which UNO type is chosen:

* *

In all other cases, the value of alternative is * ignored.

* *

This constructor cannot be used to create Type instances * that represent (sequences of) instantiated polymorphic struct types.

* * @param zClass the Java class of this type; must not be null * @param alternative controls which UNO type to choose in case of * ambiguities * * @since UDK 3.2.0 */ public Type(Class zClass, boolean alternative) { init(null, zClass, alternative, false); } /** * Constructs a new Type from the given type description. * * @param typeDescription a type description. Must not be * null. */ public Type(ITypeDescription typeDescription) { _typeName = typeDescription.getTypeName(); _typeClass = typeDescription.getTypeClass(); _iTypeDescription = typeDescription; } /** * Constructs a new Type with the given type name. * * @param typeName the name of this type; must not be null. */ public Type(String typeName) { if (typeName.startsWith("[]")) { _typeName = typeName; _typeClass = TypeClass.SEQUENCE; return; } for (int i = 0; i < __typeClassToTypeName.length; ++i) { if (__typeClassToTypeName[i].equals(typeName)) { _typeName = typeName; _typeClass = TypeClass.fromInt(i); return; } } int i = typeName.indexOf('<'); try { init( typeName, Class.forName(i < 0 ? typeName : typeName.substring(0, i)), false, i >= 0); } catch (ClassNotFoundException e) { throw new RuntimeException(e.toString()); } } /** * Constructs a new Type with the given type class. * * @param typeClass the type class of this type; must not be * null. Only type classes for simple types are allowed * here. * * @throws IllegalArgumentException if the given typeClass is * not simple (for example, a struct or an interface type). This * constructor could not find out the type name in such a case. */ public Type(TypeClass typeClass) { if(__isTypeClassPrimitive(typeClass)) { _typeClass = typeClass; _typeName = __typeClassToTypeName[typeClass.getValue()]; } else throw new IllegalArgumentException(typeClass + " is not primitive"); } /** * Gets the type class. * * @return the type class. Will never be null, but might be * UNKNOWN. */ public TypeClass getTypeClass() { return _typeClass; } /** * Gets the type name. * * @return the type name; will never be null */ public String getTypeName() { return _typeName; } /** * Gets the Java class. * * @return the type name; may be null in extreme situations * (inconsistent TypeClass, error loading a class) */ public Class getZClass() { synchronized (this) { if (_class == null) { _class = determineClass(); } } return _class; } /** * Gives the type description of this type. * * @return the type description; may be null */ public ITypeDescription getTypeDescription() { return _iTypeDescription; } /** * Sets the type description for this type. * * @param typeDescription the type description */ public void setTypeDescription(ITypeDescription typeDescription) { _iTypeDescription = typeDescription; } /** * Determines whether this UNO type is a supertype of another UNO type. * * UNO only defines the following supertype relations: * (1) A struct type t1 is a supertype of a struct type t2, if either t1 * and t2 are the same, or t1 is a direct or indirect parent of t2. * (2) An exception type t1 is a supertype of an exception type t2, if * either t1 and t2 are the same, or t1 is a direct or indirect parent * of t2. * (3) An interface type t1 is a supertype of an interface type t2, if * either t1 and t2 are the same, or t1 is a direct or indirect parent * of t2. * * Following the conventions of the Java UNO language binding, * com.sun.star.uno.Exception is not considered a supertype of * com.sun.star.uno.RuntimeException or any exception type derived from * com.sun.star.uno.RuntimeException. * * @param type some Type * @return true if this type is a supertype of the given type * * @since UDK 3.2.0 */ public boolean isSupertypeOf(Type type) { if (_typeClass != type._typeClass) { return false; } switch (_typeClass.getValue()) { case TypeClass.SEQUENCE_value: case TypeClass.ENUM_value: return _typeName.equals(type._typeName); case TypeClass.STRUCT_value: // This check exploits the fact that an instantiated polymorphic // struct type may not be the direct base of a struct type: if (_typeName.indexOf('<') >= 0 || type._typeName.indexOf('<') >= 0) { return _typeName.equals(type._typeName); } case TypeClass.EXCEPTION_value: case TypeClass.INTERFACE_value: Class c1 = getZClass(); Class c2 = type.getZClass(); return c1 != null && c2 != null && c1.isAssignableFrom(c2); default: return true; } } // @see java.lang.Object#equals public boolean equals(Object obj) { return obj instanceof Type && _typeClass == ((Type) obj)._typeClass && _typeName.equals(((Type) obj)._typeName); } // @see java.lang.Object#hashCode public int hashCode() { return _typeName.hashCode(); } // @see java.lang.Object#toString public String toString() { return "Type[" + _typeName + "]"; } private void init( String name, Class zClass, boolean alternative, boolean arguments) { TypeClass[] tc = (TypeClass[]) __javaClassToTypeClass.get(zClass); if (tc != null) { // tc only contains primitive type classes, except for // TypeClass.INTERFACE, which stands for XInterface (the alternative // interpretation of java.lang.Object): _typeClass = tc[alternative ? 1 : 0]; _typeName = _typeClass == TypeClass.INTERFACE ? XInterface.class.getName() : __typeClassToTypeName[_typeClass.getValue()]; // do not assign _class from zClass, as _class should always be // normalized (e.g., boolean.class instead of // java.lang.Boolean.class); getZClass will later calculate the // correct class when needed } else if (zClass.isArray()) { Type t = new Type(zClass.getComponentType(), alternative); _typeClass = t.getTypeClass() != TypeClass.UNKNOWN ? TypeClass.SEQUENCE : TypeClass.UNKNOWN; _typeName = "[]" + t.getTypeName(); // do not assign _class from zClass, as _class should always be // normalized (e.g., boolean[].class instead of // java.lang.Boolean[].class); getZClass will later calculate the // correct class when needed } else if (Enum.class.isAssignableFrom(zClass)) { _typeClass = zClass != Enum.class ? TypeClass.ENUM : TypeClass.UNKNOWN; _typeName = zClass.getName(); _class = zClass; } else if (Throwable.class.isAssignableFrom(zClass)) { _typeClass = com.sun.star.uno.Exception.class.isAssignableFrom(zClass) || com.sun.star.uno.RuntimeException.class.isAssignableFrom( zClass) ? TypeClass.EXCEPTION : TypeClass.UNKNOWN; _typeName = zClass.getName(); _class = zClass; } else if (zClass.isInterface()) { _typeClass = XInterface.class.isAssignableFrom(zClass) ? TypeClass.INTERFACE : TypeClass.UNKNOWN; _typeName = zClass.getName(); _class = zClass; } else if (XInterface.class.isAssignableFrom(zClass)) { // This case is needed by code that uses this constructor to // calculate the UNO type corresponding to a Java object: _typeClass = TypeClass.INTERFACE; _typeName = XInterface.class.getName(); _class = XInterface.class; } else { // assert zClass != Object.class && !zClass.isPrimitive(); _typeClass = TypeClass.STRUCT; _typeName = name == null ? zClass.getName() : name; _class = zClass; } if (arguments && _typeClass != TypeClass.STRUCT) { throw new IllegalArgumentException( zClass + " cannot have type arguments"); } } private Class determineClass() { switch (_typeClass.getValue()) { case TypeClass.VOID_value: return _typeName.equals(TYPE_NAME_VOID) ? void.class : null; case TypeClass.BOOLEAN_value: return _typeName.equals(TYPE_NAME_BOOLEAN) ? boolean.class : null; case TypeClass.BYTE_value: return _typeName.equals(TYPE_NAME_BYTE) ? byte.class : null; case TypeClass.SHORT_value: return _typeName.equals(TYPE_NAME_SHORT) ? short.class : null; case TypeClass.UNSIGNED_SHORT_value: return _typeName.equals(TYPE_NAME_UNSIGNED_SHORT) ? short.class : null; case TypeClass.LONG_value: return _typeName.equals(TYPE_NAME_LONG) ? int.class : null; case TypeClass.UNSIGNED_LONG_value: return _typeName.equals(TYPE_NAME_UNSIGNED_LONG) ? int.class : null; case TypeClass.HYPER_value: return _typeName.equals(TYPE_NAME_HYPER) ? long.class : null; case TypeClass.UNSIGNED_HYPER_value: return _typeName.equals(TYPE_NAME_UNSIGNED_HYPER) ? long.class : null; case TypeClass.FLOAT_value: return _typeName.equals(TYPE_NAME_FLOAT) ? float.class : null; case TypeClass.DOUBLE_value: return _typeName.equals(TYPE_NAME_DOUBLE) ? double.class : null; case TypeClass.CHAR_value: return _typeName.equals(TYPE_NAME_CHAR) ? char.class : null; case TypeClass.STRING_value: return _typeName.equals(TYPE_NAME_STRING) ? String.class : null; case TypeClass.TYPE_value: return _typeName.equals(TYPE_NAME_TYPE) ? Type.class : null; case TypeClass.ANY_value: return _typeName.equals(TYPE_NAME_ANY) ? Object.class : null; case TypeClass.SEQUENCE_value: StringBuffer buf = new StringBuffer(); int offset = 0; for (; _typeName.startsWith("[]", offset); offset += "[]".length()) { buf.append('['); } if (buf.length() == 0) { return null; } String base = _typeName.substring(offset); if (base.equals(TYPE_NAME_VOID)) { buf.append('V'); } else if (base.equals(TYPE_NAME_BOOLEAN)) { buf.append('Z'); } else if (base.equals(TYPE_NAME_BYTE)) { buf.append('B'); } else if (base.equals(TYPE_NAME_SHORT) || base.equals(TYPE_NAME_UNSIGNED_SHORT)) { buf.append('S'); } else if (base.equals(TYPE_NAME_LONG) || base.equals(TYPE_NAME_UNSIGNED_LONG)) { buf.append('I'); } else if (base.equals(TYPE_NAME_HYPER) || base.equals(TYPE_NAME_UNSIGNED_HYPER)) { buf.append('J'); } else if (base.equals(TYPE_NAME_FLOAT)) { buf.append('F'); } else if (base.equals(TYPE_NAME_DOUBLE)) { buf.append('D'); } else if (base.equals(TYPE_NAME_CHAR)) { buf.append('C'); } else if (base.equals(TYPE_NAME_STRING)) { buf.append("Ljava.lang.String;"); } else if (base.equals(TYPE_NAME_TYPE)) { buf.append("Lcom.sun.star.uno.Type;"); } else if (base.equals(TYPE_NAME_ANY)) { buf.append("Ljava.lang.Object;"); } else { int args = base.indexOf('<'); if (args >= 0) { base = base.substring(0, args); } Class c; try { c = Class.forName(base); } catch (ClassNotFoundException e) { return null; } if (args < 0 && new Type(c).getTypeClass() == TypeClass.UNKNOWN) { return null; } buf.append('L'); buf.append(base); buf.append(';'); } try { return Class.forName(buf.toString()); } catch (ClassNotFoundException e) { return null; } case TypeClass.ENUM_value: case TypeClass.EXCEPTION_value: case TypeClass.INTERFACE_value: { Class c; try { c = Class.forName(_typeName); } catch (ClassNotFoundException e) { return null; } return new Type(c).equals(this) ? c : null; } case TypeClass.STRUCT_value: { int args = _typeName.indexOf('<'); Class c; try { c = Class.forName( args < 0 ? _typeName : _typeName.substring(0, args)); } catch (ClassNotFoundException e) { return null; } return args >= 0 || new Type(c).equals(this) ? c : null; } default: return null; } } private static boolean __isTypeClassPrimitive(TypeClass typeClass) { return typeClass.getValue() < __typeClassToTypeName.length; } protected TypeClass _typeClass; // TODO should be final protected String _typeName; // TODO should be final protected Class _class; protected ITypeDescription _iTypeDescription; }