/************************************************************** * * 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.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Iterator; import com.sun.star.lib.uno.typedesc.TypeDescription; import com.sun.star.lib.util.WeakMap; /** * The central class needed for implementing or using UNO components in Java. * *
The methods queryInterface
and areSame
delegate
* calls to the implementing objects and are used instead of casts,
* instanceof
, ==
, and equals
.
* *
For historic reasons, this class is not final
, and has a
* public
constructor. These artifacts are considered mistakes,
* which might be corrected in a future version of this class, so client code
* should not rely on them.
public
* constructor for this class, which only has static
members.
* Also, this class might be changed to become final
in a
* future version.
*/
public UnoRuntime() {}
/**
* Generates a world wide unique identifier string.
*
* It is guaranteed that every invocation of this method generates a new * ID, which is unique within the VM. The quality of “world wide * unique” will depend on the actual implementation, you should look * at the source to determine if it meets your requirements.
* * @return a uniqueString
*/
public static String getUniqueKey() {
synchronized (uniqueKeyLock) {
if (uniqueKeyCount == Long.MAX_VALUE) {
long time;
for (time = System.currentTimeMillis(); time == uniqueKeyTime;)
{
// Conservatively sleep for 100 millisecond to wait for
// System.currentTimeMillis() to change:
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
uniqueKeyTime = time;
uniqueKeyCount = Long.MIN_VALUE;
}
return uniqueKeyHostPrefix + Long.toString(uniqueKeyTime, 16) + ":"
+ Long.toString(uniqueKeyCount++, 16);
}
}
/**
* Generates a world wide unique object identifier (OID) for the given
* Java object.
*
* It is guaranteed that subsequent calls to this method with the same * Java object will give the same ID.
* *This method is generally of little use for client code. It should be * considered a mistake that this method is published at all.
* * @param object any object for which a OID shall be generated; must not be *null
* @return the generated OID
* @see com.sun.star.uno.IQueryInterface#getOid
*/
public static String generateOid(Object object) {
String oid = null;
if (object instanceof IQueryInterface) {
oid = ((IQueryInterface) object).getOid();
}
return oid == null ? object.hashCode() + oidSuffix : oid;
}
/**
* Queries the given UNO object for the given UNO interface type.
*
* This method returns null
in case the given UNO object
* does not support the given UNO interface type (or is itself
* null
). Otherwise, a reference to a Java object implementing
* the Java interface type corresponding to the given UNO interface is
* returned. In the latter case, it is unspecified whether the returned
* Java object is the same as the given object, or is another facet of that
* UNO object.
Type
* object representing a UNO interface type
* @param object a reference to any Java object representing (a facet of) a
* UNO object; may be null
* @return a reference to the requested UNO interface type if available,
* otherwise null
* @see com.sun.star.uno.IQueryInterface#queryInterface
*/
public static Object queryInterface(Type type, Object object) {
// Gracefully handle those situations where the passed in UNO object is
// wrapped in an Any. Strictly speaking, such a situation constitutes a
// bug, but it is anticipated that such situations will arise quite
// often in practice (especially since UNO Anys containing an XInterface
// reference are not wrapped in a Java Any, but UNO Anys containing any
// other interface reference are wrapped in a Java Any, which can lead
// to confusion).
if (object instanceof Any) {
Any a = (Any) object;
if (a.getType().getTypeClass() == TypeClass.INTERFACE) {
object = a.getObject();
}
}
if (object instanceof IQueryInterface) {
object = ((IQueryInterface) object).queryInterface(type);
if (object instanceof Any) {
Any a = (Any) object;
object = a.getType().getTypeClass() == TypeClass.INTERFACE
? a.getObject() : null;
}
}
// Ensure that the object implements the requested interface type:
Class c = type.getZClass();
if (c == null || !c.isInstance(object)) {
object = null;
}
return object;
}
/**
* Queries the given UNO object for the given Java class (which must
* represent a UNO interface type).
*
* @param zInterface a Java class representing a UNO interface type
* @param object a reference to any Java object representing (a facet of) a
* UNO object; may be null
* @return a reference to the requested UNO interface type if available,
* otherwise null
* @see #queryInterface(Type, Object)
*/
@SuppressWarnings("unchecked")
public static ANY
values for equality.
Two UNO values are equal if and only if they are of the same UNO type t, and they meet the following condition, depending on t:
ANY
value.
@param any2 a Java value representing a UNO ANY
value.
@return true
if and only if the two arguments represent
equal UNO values.
*/
public static boolean areSame(Object any1, Object any2) {
Any a1 = Any.complete(any1);
Any a2 = Any.complete(any2);
Type t = a1.getType();
if (!a2.getType().equals(t)) {
return false;
}
Object v1 = a1.getObject();
Object v2 = a2.getObject();
switch (t.getTypeClass().getValue()) {
case TypeClass.VOID_value:
return true;
case TypeClass.BOOLEAN_value:
case TypeClass.BYTE_value:
case TypeClass.SHORT_value:
case TypeClass.UNSIGNED_SHORT_value:
case TypeClass.LONG_value:
case TypeClass.UNSIGNED_LONG_value:
case TypeClass.HYPER_value:
case TypeClass.UNSIGNED_HYPER_value:
case TypeClass.FLOAT_value:
case TypeClass.DOUBLE_value:
case TypeClass.CHAR_value:
case TypeClass.STRING_value:
case TypeClass.TYPE_value:
return v1.equals(v2);
case TypeClass.SEQUENCE_value:
int n = Array.getLength(v1);
if (n != Array.getLength(v2)) {
return false;
}
for (int i = 0; i < n; ++i) {
// Recursively using areSame on Java values that are (boxed)
// elements of Java arrays representing UNO sequence values,
// instead of on Java values that are representations of UNO ANY
// values, works by chance:
if (!areSame(Array.get(v1, i), Array.get(v2, i))) {
return false;
}
}
return true;
case TypeClass.ENUM_value:
return v1 == v2;
case TypeClass.STRUCT_value:
case TypeClass.EXCEPTION_value:
IFieldDescription[] fs;
try {
fs = TypeDescription.getTypeDescription(t).
getFieldDescriptions();
} catch (ClassNotFoundException e) {
throw new java.lang.RuntimeException(e.toString());
}
for (int i = 0; i< fs.length; ++i) {
Type ft = new Type(fs[i].getTypeDescription());
try {
// Recursively using areSame on Java values that are (boxed)
// fields of Java classes representing UNO struct or
// exception values, instead of on Java values that are
// representations of UNO ANY values, works by chance:
if (!areSame(
completeValue(ft, fs[i].getField().get(v1)),
completeValue(ft, fs[i].getField().get(v2))))
{
return false;
}
} catch (IllegalAccessException e) {
throw new java.lang.RuntimeException(e.toString());
}
}
return true;
case TypeClass.INTERFACE_value:
return v1 == v2
|| (v1 instanceof IQueryInterface
&& ((IQueryInterface) v1).isSame(v2))
|| (v2 instanceof IQueryInterface
&& ((IQueryInterface) v2).isSame(v1));
default:
throw new java.lang.RuntimeException(
"com.sun.star.uno.Any has bad com.sun.star.uno.TypeClass");
}
}
/**
Complete a UNO value (make sure it is no invalid null
value).
This is useful for members of parameterized type of instantiated
polymorphic struct types, as null
is a valid value there
(and only there, for all types except ANY
and interface
types).
null
.
@return the given value, or the neutral value of the given type, if the
given value was an invalid null
value.
@since UDK 3.2.3
*/
public static final Object completeValue(Type type, Object value) {
if (value != null) {
return value;
}
switch (type.getTypeClass().getValue()) {
case TypeClass.BOOLEAN_value:
return Boolean.FALSE;
case TypeClass.BYTE_value:
return new Byte((byte) 0);
case TypeClass.SHORT_value:
case TypeClass.UNSIGNED_SHORT_value:
return new Short((short) 0);
case TypeClass.LONG_value:
case TypeClass.UNSIGNED_LONG_value:
return new Integer(0);
case TypeClass.HYPER_value:
case TypeClass.UNSIGNED_HYPER_value:
return new Long(0L);
case TypeClass.FLOAT_value:
return new Float(0.0f);
case TypeClass.DOUBLE_value:
return new Double(0.0);
case TypeClass.CHAR_value:
return new Character('\u0000');
case TypeClass.STRING_value:
return "";
case TypeClass.TYPE_value:
return Type.VOID;
case TypeClass.ANY_value:
case TypeClass.INTERFACE_value:
return null;
case TypeClass.SEQUENCE_value:
return Array.newInstance(type.getZClass().getComponentType(), 0);
case TypeClass.STRUCT_value:
try {
return type.getZClass().getConstructor(null).newInstance(null);
} catch (java.lang.RuntimeException e) {
throw e;
} catch (java.lang.Exception e) {
throw new java.lang.RuntimeException(e.toString());
}
case TypeClass.ENUM_value:
try {
return type.getZClass().getMethod("getDefault", null).invoke(
null, null);
} catch (java.lang.RuntimeException e) {
throw e;
} catch (java.lang.Exception e) {
throw new java.lang.RuntimeException(e.toString());
}
default:
throw new IllegalArgumentException(
"com.sun.star.uno.UnoRuntime.completeValue called with bad"
+ " com.sun.star.uno.Type");
}
}
/**
* Gets the current context of the current thread, or null
if
* no context has been set for the current thread.
*
* The current context is thread local, which means that this method * returns the context that was last set for this thread.
* * @return the current context of the current thread, ornull
* if no context has been set for the current thread
*/
public static XCurrentContext getCurrentContext() {
return (XCurrentContext) currentContext.get();
}
/**
* Sets the current context for the current thread.
*
* The current context is thread local. To support a stacking behaviour,
* every function that sets the current context should reset it to the
* original value when exiting (for example, within a finally
* block).
null
, any
* previously set context will be removed
*/
public static void setCurrentContext(XCurrentContext context) {
// optimize this by using Java 1.5 ThreadLocal.remove if context == null
currentContext.set(context);
}
/**
* Retrieves an environment of type name
with context
* context
.
*
* Environments are held weakly by this class. If the requested
* environment already exists, this methods simply returns it. Otherwise,
* this method looks for it under
* com.sun.star.lib.uno.environments.name.name_environment
.
from
to environment
* to
.
*
* Creates a new bridge, if the requested bridge does not yet exist, and * hands the arguments to the bridge.
* *If the requested bridge does not exist, it is searched for in package
* com.sun.star.lib.uno.bridges.from_to;
* and the root classpath as
* from_to_bridge
.
from
to environment
* to
.
*
* Creates a new bridge, if the requested bridge does not yet exist, and * hands the arguments to the bridge.
* *If the requested bridge does not exist, it is searched for in package
* com.sun.star.lib.uno.bridges.from_to;
* and the root classpath as
* from_to_bridge
. The used environments
* are retrieved through getEnvironment
.
IBridge
objects
* @see com.sun.star.uno.IBridge
*
* @deprecated As of UDK 3.2.0, this method is deprecated, without
* offering a replacement.
*/
public static IBridge[] getBridges() {
ArrayList l = new ArrayList();
synchronized (bridges) {
for (Iterator i = bridges.values().iterator(); i.hasNext();) {
Object o = WeakMap.getValue(i.next());
if (o != null) {
l.add(o);
}
}
}
return (IBridge[]) l.toArray(new IBridge[l.size()]);
}
/**
* Gets a mapping from environment from
to environment
* to
.
*
* Mappings are like bridges, except that with mappings one can only map * in one direction. Mappings are here for compatibility with the binary * UNO API. Mappings are implemented as wrappers around bridges.
* * @param from the source environment * @param to the target environment * @return the requested mapping * @see com.sun.star.uno.IEnvironment * @see com.sun.star.uno.IMapping * * @deprecated As of UDK 3.2.0, this method is deprecated, without * offering a replacement. */ public static IMapping getMapping(IEnvironment from, IEnvironment to) throws java.lang.Exception { IBridge bridge; try { bridge = getBridge(from, to, null); } catch (ClassNotFoundException e) { bridge = new BridgeTurner(getBridge(to, from, null)); } return new MappingWrapper(bridge); } /** * Gets a mapping from environmentfrom
to environment
* to
.
*
* The used environments are retrieved through
* getEnvironment
.
UnoRuntime
to its initial state.
*
* Releases all references to bridges and environments.
* * @deprecated As of UDK 3.2.0, this method is deprecated, without * offering a replacement. */ static public boolean reset() { synchronized (bridges) { for (Iterator i = bridges.values().iterator(); i.hasNext();) { IBridge b = (IBridge) WeakMap.getValue(i.next()); if (b != null) { // The following call to dispose was originally made to // com.sun.star.lib.sandbox.Disposable.dispose, which cannot // throw an InterruptedException or IOException: try { b.dispose(); } catch (InterruptedException e) { Thread.currentThread().interrupted(); throw new RuntimeException( "Unexpected exception in UnoRuntime.reset: " + e); } catch (IOException e) { throw new RuntimeException( "Unexpected exception in UnoRuntime.reset: " + e); } } } bridges.clear(); } environments.clear(); return bridges.isEmpty() && environments.isEmpty(); } /** * @deprecated As of UDK 3.2.0, do not use this internal field. */ static public final boolean DEBUG = false; private static final class BridgeTurner implements IBridge { public BridgeTurner(IBridge bridge) { this.bridge = bridge; } public Object mapInterfaceTo(Object object, Type type) { return bridge.mapInterfaceFrom(object, type); } public Object mapInterfaceFrom(Object object, Type type) { return bridge.mapInterfaceTo(object, type); } public IEnvironment getSourceEnvironment() { return bridge.getTargetEnvironment(); } public IEnvironment getTargetEnvironment() { return bridge.getSourceEnvironment(); } public void acquire() { bridge.acquire(); } public void release() { bridge.release(); } public void dispose() throws InterruptedException, IOException { bridge.dispose(); } private final IBridge bridge; } private static final class MappingWrapper implements IMapping { public MappingWrapper(IBridge bridge) { this.bridge = bridge; } public Object mapInterface(Object object, Type type) { return bridge.mapInterfaceTo(object, type); } private final IBridge bridge; } private static final String uniqueKeyHostPrefix = Integer.toString(new Object().hashCode(), 16) + ":"; private static final Object uniqueKeyLock = new Object(); private static long uniqueKeyTime = System.currentTimeMillis(); private static long uniqueKeyCount = Long.MIN_VALUE; private static final String oidSuffix = ";java[];" + getUniqueKey(); private static final ThreadLocal currentContext = new ThreadLocal(); private static final WeakMap environments = new WeakMap(); private static final WeakMap bridges = new WeakMap(); }