diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /ridljar/com | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ridljar/com')
95 files changed, 20251 insertions, 0 deletions
diff --git a/ridljar/com/sun/star/comp/bridgefactory/BridgeFactory.java b/ridljar/com/sun/star/comp/bridgefactory/BridgeFactory.java new file mode 100644 index 000000000..031e60d3f --- /dev/null +++ b/ridljar/com/sun/star/comp/bridgefactory/BridgeFactory.java @@ -0,0 +1,212 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.bridgefactory; + +import java.math.BigInteger; +import java.util.ArrayList; + +import com.sun.star.bridge.BridgeExistsException; +import com.sun.star.bridge.XBridge; +import com.sun.star.bridge.XBridgeFactory; +import com.sun.star.bridge.XInstanceProvider; +import com.sun.star.comp.loader.FactoryHelper; +import com.sun.star.connection.XConnection; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; +import com.sun.star.uno.IBridge; +import com.sun.star.uno.UnoRuntime; + + +/** + * The BridgeFactory class implements the <code>XBridgeFactory</code> Interface. + * + * <p>It wraps the <code>UnoRuntime#getBridgeByName</code>method and delivers a + * XBridge component.</p> + * + * <p>This component is only usable for remote bridges.</p> + * + * @see com.sun.star.uno.UnoRuntime + * @since UDK1.0 + */ +public class BridgeFactory implements XBridgeFactory/*, XEventListener*/ { + private static final boolean DEBUG = false; + + /** + * The name of the service, the <code>JavaLoader</code> accesses this through + * reflection. + */ + public static final String __serviceName = "com.sun.star.bridge.BridgeFactory"; + + /** + * Gives a factory for creating the service. + * + * <p>This method is called by the <code>JavaLoader</code>.</p> + * + * @param implName the name of the implementation for which a service is desired. + * @param multiFactory the service manager to be uses if needed. + * @param regKey the registryKey. + * @return returns a <code>XSingleServiceFactory</code> for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals(BridgeFactory.class.getName()) ) + xSingleServiceFactory = FactoryHelper.getServiceFactory(BridgeFactory.class, + multiFactory, + regKey); + + return xSingleServiceFactory; + } + + /** + * Creates a remote bridge and memorizes it under <code>sName</code>. + * + * @param sName the name to memorize the bridge. + * @param sProtocol the protocol the bridge should use. + * @param anInstanceProvider the instance provider. + * @return the bridge. + * + * @see com.sun.star.bridge.XBridgeFactory + */ + public XBridge createBridge(String sName, String sProtocol, XConnection aConnection, XInstanceProvider anInstanceProvider) throws + BridgeExistsException, + com.sun.star.lang.IllegalArgumentException, + com.sun.star.uno.RuntimeException + { + boolean hasName = sName.length() != 0; + Object context = hasName ? sName : new UniqueToken(); + // UnoRuntime.getBridgeByName internally uses context.toString() to + // distinguish bridges, so the result of + // new UniqueToken().toString() might clash with an explicit + // sName.toString(), but the UnoRuntime bridge management is + // obsolete anyway and should be removed + + // do not create a new bridge, if one already exists + if (hasName) { + IBridge iBridges[] = UnoRuntime.getBridges(); + for(int i = 0; i < iBridges.length; ++ i) { + XBridge xBridge = UnoRuntime.queryInterface(XBridge.class, iBridges[i]); + + if(xBridge != null && xBridge.getName().equals(sName)) { + throw new BridgeExistsException(sName + " already exists"); + } + } + } + + XBridge xBridge; + + try { + IBridge iBridge = UnoRuntime.getBridgeByName("java", context, "remote", context, hasName ? new Object[]{sProtocol, aConnection, anInstanceProvider, sName} : new Object[]{sProtocol, aConnection, anInstanceProvider}); + + xBridge = UnoRuntime.queryInterface(XBridge.class, iBridge); + } + catch (Exception e) { + throw new com.sun.star.lang.IllegalArgumentException(e, e.getMessage()); + } + + if(DEBUG) System.err.println("##### " + getClass().getName() + ".createBridge:" + sName + " " + sProtocol + " " + aConnection + " " + anInstanceProvider + " " + xBridge); + + return xBridge; + } + + /** + * Gets a remote bridge which must already exist. + * + * @param sName the name of the bridge. + * @return the bridge. + * @see com.sun.star.bridge.XBridgeFactory + */ + public XBridge getBridge(String sName) throws com.sun.star.uno.RuntimeException { + XBridge xBridge = null; + + IBridge iBridges[] = UnoRuntime.getBridges(); + for(int i = 0; i < iBridges.length; ++ i) { + xBridge = UnoRuntime.queryInterface(XBridge.class, iBridges[i]); + + if(xBridge != null) { + if(xBridge.getName().equals(sName)) + break; + + else + xBridge = null; + } + } + + + if(DEBUG) System.err.println("##### " + getClass().getName() + ".getBridge:" + sName + " " + xBridge); + + return xBridge; + } + + /** + * Gives all created bridges. + * + * @return the bridges. + * @see com.sun.star.bridge.XBridgeFactory + */ + public synchronized XBridge[] getExistingBridges() throws com.sun.star.uno.RuntimeException { + ArrayList<XBridge> vector = new ArrayList<XBridge>(); + + IBridge iBridges[] = UnoRuntime.getBridges(); + for(int i = 0; i < iBridges.length; ++ i) { + XBridge xBridge = UnoRuntime.queryInterface(XBridge.class, iBridges[i]); + + if(xBridge != null) + vector.add(xBridge); + } + + XBridge xBridges[]= new XBridge[vector.size()]; + for(int i = 0; i < vector.size(); ++ i) + xBridges[i] = vector.get(i); + + return xBridges; + } + + private static final class UniqueToken { + public UniqueToken() { + synchronized (UniqueToken.class) { + token = counter.toString(); + counter = counter.add(BigInteger.ONE); + } + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + * @see java.lang.Object#toString + */ + @Override + public String toString() { + return token; + } + + private final String token; + private static BigInteger counter = BigInteger.ZERO; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/connections/Acceptor.java b/ridljar/com/sun/star/comp/connections/Acceptor.java new file mode 100644 index 000000000..996eaeb85 --- /dev/null +++ b/ridljar/com/sun/star/comp/connections/Acceptor.java @@ -0,0 +1,153 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.connections; + +import com.sun.star.comp.loader.FactoryHelper; +import com.sun.star.connection.AlreadyAcceptingException; +import com.sun.star.connection.ConnectionSetupException; +import com.sun.star.connection.XAcceptor; +import com.sun.star.connection.XConnection; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +/** + * A component that implements the <code>XAcceptor</code> interface. + * + * <p>The <code>Acceptor</code> is a general component, that uses less general + * components (like <code>com.sun.star.connection.socketAcceptor</code>) to + * implement its functionality.</p> + * + * @see com.sun.star.connection.XAcceptor + * @see com.sun.star.connection.XConnection + * @see com.sun.star.connection.XConnector + * @see com.sun.star.comp.loader.JavaLoader + * + * @since UDK 1.0 + */ +public final class Acceptor implements XAcceptor { + /** + * The name of the service. + * + * <p>The <code>JavaLoader</code> accesses this through reflection.</p> + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.Acceptor"; + + /** + * Returns a factory for creating the service. + * + * <p>This method is called by the <code>JavaLoader</code>.</p> + * + * @param implName the name of the implementation for which a service is + * requested. + * @param multiFactory the service manager to be used (if needed). + * @param regKey the registry key. + * @return an <code>XSingleServiceFactory</code> for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) + { + return implName.equals(Acceptor.class.getName()) + ? FactoryHelper.getServiceFactory(Acceptor.class, __serviceName, + multiFactory, regKey) + : null; + } + + /** + * Constructs a new <code>Acceptor</code> that uses the given service + * factory to create a specific <code>XAcceptor</code>. + * + * @param serviceFactory the service factory to use. + */ + public Acceptor(XMultiServiceFactory serviceFactory) { + this.serviceFactory = serviceFactory; + } + + /** + * Accepts a connection request via the given connection type. + * + * <p>This call blocks until a connection has been established.</p> + * + * <p>The connection description has the following format: + * <code><var>type</var></code><!-- + * -->*(<code><var>key</var>=<var>value</var></code>). + * The specific <code>XAcceptor</code> implementation is instantiated + * through the service factory as + * <code>com.sun.star.connection.<var>type</var>Acceptor</code> (with + * <code><var>type</var></code> in lower case).</p> + * + * @param connectionDescription the description of the connection. + * @return an <code>XConnection</code> to the client. + * + * @see com.sun.star.connection.XConnection + * @see com.sun.star.connection.XConnector + */ + public XConnection accept(String connectionDescription) throws + AlreadyAcceptingException, ConnectionSetupException, + com.sun.star.lang.IllegalArgumentException + { + if (DEBUG) { + System.err.println("##### " + getClass().getName() + ".accept(" + + connectionDescription + ")"); + } + XAcceptor acc; + synchronized (this) { + if (acceptor == null) { + acceptor = (XAcceptor) Implementation.getConnectionService( + serviceFactory, connectionDescription, XAcceptor.class, + "Acceptor"); + acceptingDescription = connectionDescription; + } else if (!connectionDescription.equals(acceptingDescription)) { + throw new AlreadyAcceptingException(acceptingDescription + + " vs. " + + connectionDescription); + } + acc = acceptor; + } + return acc.accept(connectionDescription); + } + + /** + * + * @see com.sun.star.connection.XAcceptor#stopAccepting + */ + public void stopAccepting() { + XAcceptor acc; + synchronized (this) { + acc = acceptor; + } + acc.stopAccepting(); + } + + private static final boolean DEBUG = false; + + private final XMultiServiceFactory serviceFactory; + + private XAcceptor acceptor = null; + private String acceptingDescription; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + diff --git a/ridljar/com/sun/star/comp/connections/Connector.java b/ridljar/com/sun/star/comp/connections/Connector.java new file mode 100644 index 000000000..6ffab9eea --- /dev/null +++ b/ridljar/com/sun/star/comp/connections/Connector.java @@ -0,0 +1,130 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.connections; + +import com.sun.star.comp.loader.FactoryHelper; +import com.sun.star.connection.ConnectionSetupException; +import com.sun.star.connection.NoConnectException; +import com.sun.star.connection.XConnection; +import com.sun.star.connection.XConnector; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +/** + * A component that implements the <code>XConnector</code> interface. + * + * <p>The <code>Connector</code> is a general component, that uses less general + * components (like <code>com.sun.star.connection.socketConnector</code>) to + * implement its functionality.</p> + * + * @see com.sun.star.connection.XAcceptor + * @see com.sun.star.connection.XConnection + * @see com.sun.star.connection.XConnector + * @see com.sun.star.comp.loader.JavaLoader + * + * @since UDK 1.0 + */ +public class Connector implements XConnector { + /** + * The name of the service. + * + * <p>The <code>JavaLoader</code> accesses this through reflection.</p> + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.Connector"; + + /** + * Returns a factory for creating the service. + * + * <p>This method is called by the <code>JavaLoader</code>.</p> + * + * @param implName the name of the implementation for which a service is + * requested. + * @param multiFactory the service manager to be used (if needed). + * @param regKey the registry key. + * @return an <code>XSingleServiceFactory</code> for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) + { + return implName.equals(Connector.class.getName()) + ? FactoryHelper.getServiceFactory(Connector.class, __serviceName, + multiFactory, regKey) + : null; + } + + /** + * Constructs a new <code>Connector</code> that uses the given service + * factory to create a specific <code>XConnector</code>. + * + * @param serviceFactory the service factory to use. + */ + public Connector(XMultiServiceFactory serviceFactory) { + this.serviceFactory = serviceFactory; + } + + /** + * Connects via the given connection type to a waiting server. + * + * <p>The connection description has the following format: + * <code><var>type</var></code><!-- + * -->*(<code><var>key</var>=<var>value</var></code>). + * The specific <code>XConnector</code> implementation is instantiated + * through the service factory as + * <code>com.sun.star.connection.<var>type</var>Connector</code> (with + * <code><var>type</var></code> in lower case).</p> + * + * @param connectionDescription the description of the connection. + * @return an <code>XConnection</code> to the server. + * + * @see com.sun.star.connection.XAcceptor + * @see com.sun.star.connection.XConnection + */ + public synchronized XConnection connect(String connectionDescription) + throws NoConnectException, ConnectionSetupException + { + if (DEBUG) { + System.err.println("##### " + getClass().getName() + ".connect(" + + connectionDescription + ")"); + } + if (connected) { + throw new ConnectionSetupException("already connected"); + } + XConnection con + = ((XConnector) Implementation.getConnectionService( + serviceFactory, connectionDescription, XConnector.class, + "Connector")).connect(connectionDescription); + connected = true; + return con; + } + + private static final boolean DEBUG = false; + + private final XMultiServiceFactory serviceFactory; + + private boolean connected = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/connections/ConstantInstanceProvider.java b/ridljar/com/sun/star/comp/connections/ConstantInstanceProvider.java new file mode 100644 index 000000000..6c2fcb2d8 --- /dev/null +++ b/ridljar/com/sun/star/comp/connections/ConstantInstanceProvider.java @@ -0,0 +1,118 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.connections; + +import com.sun.star.bridge.XInstanceProvider; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.comp.loader.FactoryHelper; + + +/** + * The <code>ConstantInstanceProvider</code> is a component + * that implements the <code>XInstanceProvider</code> Interface. + * + * @see com.sun.star.bridge.XBridge + * @see com.sun.star.bridge.XBridgeFactory + * @see com.sun.star.bridge.XInstanceProvider + * @see com.sun.star.comp.loader.JavaLoader + * @since UDK1.0 + */ +public class ConstantInstanceProvider implements XInstanceProvider { + /** + * When set to true, enables various debugging output. + */ + public static final boolean DEBUG = false; + + /** + * The name of the service, the <code>JavaLoader</code> accesses this through + * reflection. + */ + private static final String __serviceName = "com.sun.star.comp.connection.InstanceProvider"; + + /** + * Gives a factory for creating the service. + * <p>This method is called by the <code>JavaLoader</code>.</p> + * + * @param implName the name of the implementation for which a service is desired. + * @param multiFactory the service manager to be uses if needed. + * @param regKey the registryKey. + * @return returns a <code>XSingleServiceFactory</code> for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals(ConstantInstanceProvider.class.getName()) ) + xSingleServiceFactory = FactoryHelper.getServiceFactory(ConstantInstanceProvider.class, + __serviceName, + multiFactory, + regKey); + + return xSingleServiceFactory; + } + + protected XMultiServiceFactory _serviceManager; + protected String _serviceName; + protected Object _instance; + + + public void setInstance(String serviceName) throws com.sun.star.uno.Exception { + _instance = _serviceManager.createInstance(serviceName); + _serviceName = serviceName; + } + + /** + * Constructs a new <code>ConstantInstanceProvider</code>. + * <p>Uses the provided ServiceManager as the provided instance.</p> + * + * @param serviceManager the provided service manager + */ + public ConstantInstanceProvider(XMultiServiceFactory serviceManager) { + _serviceManager = serviceManager; + + _serviceName = "SERVICEMANAGER"; + _instance = serviceManager; + } + + /** + * Gives an object for the passed instance name. + * + * @return the desired instance + * @param sInstanceName the name of the desired instance + */ + public Object getInstance(String sInstanceName) throws com.sun.star.container.NoSuchElementException, com.sun.star.uno.RuntimeException { + Object result = sInstanceName.equals(_serviceName) ? _instance : null; + + if(DEBUG) System.err.println("##### " + getClass().getName() + ".getInstance(" + sInstanceName + "):" + result); + + return result; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/connections/Implementation.java b/ridljar/com/sun/star/comp/connections/Implementation.java new file mode 100644 index 000000000..94cabe253 --- /dev/null +++ b/ridljar/com/sun/star/comp/connections/Implementation.java @@ -0,0 +1,95 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.connections; + +import com.sun.star.connection.ConnectionSetupException; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; + +/** + * Helper class for <code>Acceptor</code> and <code>Connector</code>. + */ +final class Implementation { + /** + * Instantiate a service for a given connection type. + * + * @param factory the service factory used to instantiate the requested + * service. + * @param description has the following format: + * <code><var>type</var></code><!-- + * -->*(<code><var>key</var>=<var>value</var></code>). + * The specific service implementation is instantiated through the + * service factory as + * <code>com.sun.star.connection.<var>type</var>service<var></var><!-- + * --></code> + * (with <code><var>type</var></code> in lower case, and + * <code><var>service</var></code> either <code>Acceptor</code> or + * <code>Connector</code>). + * @param serviceClass the IDL interface type for which to query the + * requested service. + * @param serviceType must be either <code>Acceptor</code> or + * <code>Connector</code>. + * @return an instance of the requested service. Never returns + * <code>null</code>. + * @throws ConnectionSetupException if the requested service can not be + * found, or cannot be instantiated. + */ + public static Object getConnectionService(XMultiServiceFactory factory, + String description, + Class<?> serviceClass, + String serviceType) + throws ConnectionSetupException + { + int i = description.indexOf(','); + String type + = (i < 0 ? description : description.substring(0, i)).toLowerCase(); + Object service = null; + try { + service = UnoRuntime.queryInterface( + serviceClass, + factory.createInstance("com.sun.star.connection." + type + + serviceType)); + } catch (RuntimeException e) { + throw e; + } catch (com.sun.star.uno.Exception e) { + } + if (service == null) { + // As a fallback, also try to instantiate the service from the + // com.sun.star.lib.connections package structure: + try { + service + = Class.forName("com.sun.star.lib.connections." + type + + "." + type + serviceType).newInstance(); + } catch (ClassNotFoundException e) { + } catch (IllegalAccessException e) { + } catch (InstantiationException e) { + } + } + if (service == null) { + throw new ConnectionSetupException("no " + serviceType + " for " + + type); + } + return service; + } + + private Implementation() {} // do not instantiate +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/connections/PipedConnection.java b/ridljar/com/sun/star/comp/connections/PipedConnection.java new file mode 100644 index 000000000..631cfae40 --- /dev/null +++ b/ridljar/com/sun/star/comp/connections/PipedConnection.java @@ -0,0 +1,259 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.connections; + + +import com.sun.star.comp.loader.FactoryHelper; +import com.sun.star.connection.XConnection; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +/** + * The <code>PipedConnection</code> is a component that implements the + * <code>XConnection</code> Interface. + * <p>It is useful for <code>Thread</code> communication in one Process.</p> + * + * @see com.sun.star.connection.XConnection + * @see com.sun.star.comp.loader.JavaLoader + * @since UDK1.0 + */ +public class PipedConnection implements XConnection { + /** + * When set to true, enables various debugging output. + */ + public static final boolean DEBUG = false; + + /** + * The name of the service, the <code>JavaLoader</code> accesses this through + * reflection. + */ + private static final String __serviceName = "com.sun.star.connection.PipedConnection"; + + /** + * Gives a factory for creating the service. + * <p>This method is called by the <code>JavaLoader</code>.</p> + * + * @param implName the name of the implementation for which a service is desired. + * @param multiFactory the service manager to be uses if needed. + * @param regKey the registryKey. + * @return returns a <code>XSingleServiceFactory</code> for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals(PipedConnection.class.getName()) ) + xSingleServiceFactory = FactoryHelper.getServiceFactory(PipedConnection.class, + __serviceName, + multiFactory, + regKey); + + return xSingleServiceFactory; + } + + /** + * The amount of time in milliseconds, to wait to see check the buffers. + */ + protected static final int __waitTime = 10000; + + protected byte _buffer[] = new byte[4096]; + protected int _in, + _out; + protected boolean _closed; + protected PipedConnection _otherSide; + + /** + * Constructs a new <code>PipedConnection</code>, sees if there is another + * side, which it should be connected to. + * + * @param args Another side could be in index 0. + */ + public PipedConnection(Object args[]) throws com.sun.star.uno.RuntimeException { + if (DEBUG) System.err.println("##### " + getClass().getName() + " - instantiated"); + + _otherSide = (args.length == 1) ? (PipedConnection)args[0] : null; + if(_otherSide != null) { + if(_otherSide == this) + throw new RuntimeException("can not connect to myself"); + + _otherSide._otherSide = this; + } + } + + /** + * This is a private method, used to communicate internal in the pipe. + */ + private synchronized void receive(byte aData[]) throws com.sun.star.io.IOException { + int bytesWritten = 0; + + if(DEBUG) System.err.println("##### PipedConnection.receive - bytes:" + aData.length + " at:" + _out); + + while(bytesWritten < aData.length) { + // wait until it is not full anymore + while(_out == (_in - 1) || (_in == 0 && _out == _buffer.length - 1)) { + try { + notify(); // the buffer is full, signal it + + wait(__waitTime); + } + catch(InterruptedException interruptedException) { + throw new com.sun.star.io.IOException(interruptedException); + } + } + + if(_closed) throw new com.sun.star.io.IOException("connection has been closed"); + + int bytes ; + + if(_out < _in) { + bytes = Math.min(aData.length - bytesWritten, _in - _out - 1); + + System.arraycopy(aData, bytesWritten, _buffer, _out, bytes); + } + else { + if(_in > 0){ + bytes = Math.min(aData.length - bytesWritten, _buffer.length - _out); + } + else { + bytes = Math.min(aData.length - bytesWritten, _buffer.length - _out - 1); + } + + System.arraycopy(aData, bytesWritten, _buffer, _out, bytes); + } + + bytesWritten += bytes; + _out += bytes; + if(_out >= _buffer.length) + _out = 0; + } + } + + /** + * Read the required number of bytes. + * + * @param aReadBytes the out parameter, where the bytes have to be placed. + * @param nBytesToRead the number of bytes to read. + * @return the number of bytes read. + * + * @see com.sun.star.connection.XConnection#read + */ + public synchronized int read(/*OUT*/byte[][] aReadBytes, int nBytesToRead) throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException { + aReadBytes[0] = new byte[nBytesToRead]; + + if(DEBUG) System.err.println("##### PipedConnection.read - bytes:" + nBytesToRead + " at:" + _in); + + // loop while not all bytes read or when closed but there is still data + while(nBytesToRead > 0 && (_in != _out || !_closed)) { + while(_in == _out && !_closed) { + try { + notify(); // the buffer is empty, signal it + + wait(__waitTime); // we wait for data or for the pipe to be closed + } + catch(InterruptedException interruptedException) { + throw new com.sun.star.io.IOException(interruptedException); + } + } + + if(_in < _out) { + int bytes = Math.min(nBytesToRead, _out - _in); + + System.arraycopy(_buffer, _in, aReadBytes[0], aReadBytes[0].length - nBytesToRead, bytes); + + nBytesToRead -= bytes; + _in += bytes; + } + else if(_in > _out) { + int bytes = Math.min(nBytesToRead, _buffer.length - _in); + + System.arraycopy(_buffer, _in, aReadBytes[0], aReadBytes[0].length - nBytesToRead, bytes); + + nBytesToRead -= bytes; + _in += bytes; + if(_in >= _buffer.length) + _in = 0; + } + } + + if(nBytesToRead > 0) { // not all bytes read + byte tmp[] = new byte[aReadBytes[0].length - nBytesToRead]; + System.arraycopy(aReadBytes[0], 0, tmp, 0, tmp.length); + + aReadBytes[0] = tmp; + } + + return aReadBytes[0].length; + } + + /** + * Write bytes. + * + * @param aData the bytes to write. + * @see com.sun.star.connection.XConnection#write + */ + public void write(byte aData[]) throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException { + _otherSide.receive(aData); + } + + /** + * Flushes the buffer, notifies if necessary the other side that new data has + * arrived. + * + * @see com.sun.star.connection.XConnection#flush + */ + public void flush() throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException { + synchronized(_otherSide) { + _otherSide.notify(); + } + } + + /** + * Closes the pipe. + * + * @see com.sun.star.connection.XConnection#close() + */ + public synchronized void close() throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException { + if(!_closed) { + _closed = true; + + _otherSide.close(); + + notify(); + } + } + + /** + * Gives a description of this pipe. + * + * @return the description. + * @see com.sun.star.connection.XConnection#getDescription + */ + public String getDescription() throws com.sun.star.uno.RuntimeException { + return getClass().getName(); + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/helper/Bootstrap.java b/ridljar/com/sun/star/comp/helper/Bootstrap.java new file mode 100644 index 000000000..edf21d7d2 --- /dev/null +++ b/ridljar/com/sun/star/comp/helper/Bootstrap.java @@ -0,0 +1,429 @@ +// -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.helper; + +import com.sun.star.bridge.UnoUrlResolver; +import com.sun.star.bridge.XUnoUrlResolver; +import com.sun.star.comp.loader.JavaLoader; +import com.sun.star.comp.servicemanager.ServiceManager; +import com.sun.star.container.XSet; +import com.sun.star.lang.XInitialization; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lib.util.NativeLibraryLoader; +import com.sun.star.loader.XImplementationLoader; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.Random; + +/** Bootstrap offers functionality to obtain a context or simply + a service manager. + The service manager can create a few basic services, whose implementations are: + <ul> + <li>com.sun.star.comp.loader.JavaLoader</li> + <li>com.sun.star.comp.urlresolver.UrlResolver</li> + <li>com.sun.star.comp.bridgefactory.BridgeFactory</li> + <li>com.sun.star.comp.connections.Connector</li> + <li>com.sun.star.comp.connections.Acceptor</li> + <li>com.sun.star.comp.servicemanager.ServiceManager</li> + </ul> + + Other services can be inserted into the service manager by + using its XSet interface: + <pre> + XSet xSet = UnoRuntime.queryInterface( XSet.class, aMultiComponentFactory ); + // insert the service manager + xSet.insert( aSingleComponentFactory ); + </pre> +*/ +public class Bootstrap { + + private static void insertBasicFactories( + XSet xSet, XImplementationLoader xImpLoader ) + throws Exception + { + // insert the factory of the loader + xSet.insert( xImpLoader.activate( + "com.sun.star.comp.loader.JavaLoader", null, null, null ) ); + + // insert the factory of the URLResolver + xSet.insert( xImpLoader.activate( + "com.sun.star.comp.urlresolver.UrlResolver", null, null, null ) ); + + // insert the bridgefactory + xSet.insert( xImpLoader.activate( + "com.sun.star.comp.bridgefactory.BridgeFactory", null, null, null ) ); + + // insert the connector + xSet.insert( xImpLoader.activate( + "com.sun.star.comp.connections.Connector", null, null, null ) ); + + // insert the acceptor + xSet.insert( xImpLoader.activate( + "com.sun.star.comp.connections.Acceptor", null, null, null ) ); + } + + /** + * Returns an array of default commandline options to start bootstrapped + * instance of soffice with. You may use it in connection with bootstrap + * method for example like this: + * <pre> + * List list = Arrays.asList( Bootstrap.getDefaultOptions() ); + * list.remove("--nologo"); + * list.remove("--nodefault"); + * list.add("--invisible"); + * + * Bootstrap.bootstrap( list.toArray( new String[list.size()] ); + * </pre> + * + * @return an array of default commandline options + * @see #bootstrap( String[] ) + * @since LibreOffice 5.1 + */ + public static final String[] getDefaultOptions() + { + return new String[] + { + "--nologo", + "--nodefault", + "--norestore", + "--nolockcheck" + }; + } + + /** + backwards compatibility stub. + @param context_entries the hash table contains mappings of entry names (type string) to + context entries (type class ComponentContextEntry). + @throws Exception if things go awry. + @return a new context. + */ + public static XComponentContext createInitialComponentContext( Hashtable<String, Object> context_entries ) + throws Exception + { + return createInitialComponentContext((Map<String, Object>) context_entries); + } + /** Bootstraps an initial component context with service manager and basic + jurt components inserted. + @param context_entries the hash table contains mappings of entry names (type string) to + context entries (type class ComponentContextEntry). + @throws Exception if things go awry. + @return a new context. + */ + public static XComponentContext createInitialComponentContext( Map<String, Object> context_entries ) + throws Exception + { + ServiceManager xSMgr = new ServiceManager(); + + XImplementationLoader xImpLoader = UnoRuntime.queryInterface( + XImplementationLoader.class, new JavaLoader() ); + XInitialization xInit = UnoRuntime.queryInterface( + XInitialization.class, xImpLoader ); + Object[] args = new Object [] { xSMgr }; + xInit.initialize( args ); + + // initial component context + if (context_entries == null) + context_entries = new HashMap<String,Object>( 1 ); + // add smgr + context_entries.put( + "/singletons/com.sun.star.lang.theServiceManager", + new ComponentContextEntry( null, xSMgr ) ); + // ... xxx todo: add standard entries + XComponentContext xContext = new ComponentContext( context_entries, null ); + + xSMgr.setDefaultContext(xContext); + + XSet xSet = UnoRuntime.queryInterface( XSet.class, xSMgr ); + // insert basic jurt factories + insertBasicFactories( xSet, xImpLoader ); + + return xContext; + } + + /** + * Bootstraps a servicemanager with the jurt base components registered. + * + * See also UNOIDL <code>com.sun.star.lang.ServiceManager</code>. + * + * @throws Exception if things go awry. + * @return a freshly bootstrapped service manager + */ + public static XMultiServiceFactory createSimpleServiceManager() throws Exception + { + return UnoRuntime.queryInterface( + XMultiServiceFactory.class, createInitialComponentContext( (Map<String, Object>) null ).getServiceManager() ); + } + + + /** Bootstraps the initial component context from a native UNO installation. + + @throws Exception if things go awry. + @return a freshly bootstrapped component context. + + See also + <code>cppuhelper/defaultBootstrap_InitialComponentContext()</code>. + */ + public static final XComponentContext defaultBootstrap_InitialComponentContext() + throws Exception + { + return defaultBootstrap_InitialComponentContext( (String) null, (Map<String,String>) null ); + } + /** + * Backwards compatibility stub. + * + * @param ini_file + * ini_file (may be null: uno.rc besides cppuhelper lib) + * @param bootstrap_parameters + * bootstrap parameters (maybe null) + * + * @throws Exception if things go awry. + * @return a freshly bootstrapped component context. + */ + public static final XComponentContext defaultBootstrap_InitialComponentContext( + String ini_file, Hashtable<String,String> bootstrap_parameters ) + throws Exception + { + return defaultBootstrap_InitialComponentContext(ini_file, (Map<String,String>) bootstrap_parameters); + + } + /** Bootstraps the initial component context from a native UNO installation. + + See also + <code>cppuhelper/defaultBootstrap_InitialComponentContext()</code>. + + @param ini_file + ini_file (may be null: uno.rc besides cppuhelper lib) + @param bootstrap_parameters + bootstrap parameters (maybe null) + + @throws Exception if things go awry. + @return a freshly bootstrapped component context. + */ + public static final XComponentContext defaultBootstrap_InitialComponentContext( + String ini_file, Map<String,String> bootstrap_parameters ) + throws Exception + { + // jni convenience: easier to iterate over array than calling Hashtable + String pairs [] = null; + if (null != bootstrap_parameters) + { + pairs = new String [ 2 * bootstrap_parameters.size() ]; + int n = 0; + for (Map.Entry<String, String> bootstrap_parameter : bootstrap_parameters.entrySet()) { + pairs[ n++ ] = bootstrap_parameter.getKey(); + pairs[ n++ ] = bootstrap_parameter.getValue(); + } + } + + if (! m_loaded_juh) + { + if ("The Android Project".equals(System.getProperty("java.vendor"))) + { + // Find out if we are configured with DISABLE_DYNLOADING or + // not. Try to load the lo-bootstrap shared library which + // won't exist in the DISABLE_DYNLOADING case. (And which will + // be already loaded otherwise, so nothing unexpected happens + // that case.) Yeah, this would be simpler if I just could be + // bothered to keep a separate branch for DISABLE_DYNLOADING + // on Android, merging in master periodically, until I know + // for sure whether it is what I want, or not. + + boolean disable_dynloading = false; + try { + System.loadLibrary( "lo-bootstrap" ); + } catch ( UnsatisfiedLinkError e ) { + disable_dynloading = true; + } + + if (!disable_dynloading) + { + NativeLibraryLoader.loadLibrary( Bootstrap.class.getClassLoader(), "juh" ); + } + } + else + { + NativeLibraryLoader.loadLibrary( Bootstrap.class.getClassLoader(), "juh" ); + } + m_loaded_juh = true; + } + return UnoRuntime.queryInterface( + XComponentContext.class, + cppuhelper_bootstrap( + ini_file, pairs, Bootstrap.class.getClassLoader() ) ); + } + + private static boolean m_loaded_juh = false; + private static native Object cppuhelper_bootstrap( + String ini_file, String bootstrap_parameters [], ClassLoader loader ) + throws Exception; + + /** + * Bootstraps the component context from a UNO installation. + * + * @throws BootstrapException if things go awry. + * + * @return a bootstrapped component context. + * + * @since UDK 3.1.0 + */ + public static final XComponentContext bootstrap() + throws BootstrapException { + + String[] defaultArgArray = getDefaultOptions(); + return bootstrap( defaultArgArray ); + } + + /** + * Bootstraps the component context from a UNO installation. + * + * @param argArray + * an array of strings - commandline options to start instance of + * soffice with + * @see #getDefaultOptions() + * + * @throws BootstrapException if things go awry. + * + * @return a bootstrapped component context. + * + * @since LibreOffice 5.1 + */ + public static final XComponentContext bootstrap( String[] argArray ) + throws BootstrapException { + + XComponentContext xContext = null; + + try { + // create default local component context + XComponentContext xLocalContext = + createInitialComponentContext( (Map<String, Object>) null ); + if ( xLocalContext == null ) + throw new BootstrapException( "no local component context!" ); + + // find office executable relative to this class's class loader + String sOffice = + System.getProperty( "os.name" ).startsWith( "Windows" ) ? + "soffice.exe" : "soffice"; + File fOffice = NativeLibraryLoader.getResource( + Bootstrap.class.getClassLoader(), sOffice ); + if ( fOffice == null ) + throw new BootstrapException( "no office executable found!" ); + + // create random pipe name + String sPipeName = "uno" + + Long.toString(randomPipeName.nextLong() & 0x7fffffffffffffffL); + + // create call with arguments + String[] cmdArray = new String[ argArray.length + 2 ]; + cmdArray[0] = fOffice.getPath(); + cmdArray[1] = ( "--accept=pipe,name=" + sPipeName + ";urp;" ); + + System.arraycopy( argArray, 0, cmdArray, 2, argArray.length ); + + // start office process + Process p = Runtime.getRuntime().exec( cmdArray ); + pipe( p.getInputStream(), System.out, "CO> " ); + pipe( p.getErrorStream(), System.err, "CE> " ); + + // initial service manager + XMultiComponentFactory xLocalServiceManager = + xLocalContext.getServiceManager(); + if ( xLocalServiceManager == null ) + throw new BootstrapException( "no initial service manager!" ); + + // create a URL resolver + XUnoUrlResolver xUrlResolver = + UnoUrlResolver.create( xLocalContext ); + + // connection string + String sConnect = "uno:pipe,name=" + sPipeName + + ";urp;StarOffice.ComponentContext"; + + // wait until office is started + for (int i = 0;; ++i) { + try { + // try to connect to office + Object context = xUrlResolver.resolve( sConnect ); + xContext = UnoRuntime.queryInterface( + XComponentContext.class, context); + if ( xContext == null ) + throw new BootstrapException( "no component context!" ); + break; + } catch ( com.sun.star.connection.NoConnectException ex ) { + // Wait 500 ms, then try to connect again, but do not wait + // longer than 5 min (= 600 * 500 ms) total: + if (i == 600) { + throw new BootstrapException(ex); + } + Thread.sleep( 500 ); + } + } + } catch ( BootstrapException e ) { + throw e; + } catch ( java.lang.RuntimeException e ) { + throw e; + } catch ( java.lang.Exception e ) { + throw new BootstrapException( e ); + } + + return xContext; + } + + private static final Random randomPipeName = new Random(); + + private static void pipe( + final InputStream in, final PrintStream out, final String prefix ) { + + new Thread( "Pipe: " + prefix) { + @Override + public void run() { + try { + BufferedReader r = new BufferedReader( + new InputStreamReader(in, "UTF-8") ); + + for ( ; ; ) { + String s = r.readLine(); + if ( s == null ) { + break; + } + out.println( prefix + s ); + } + } catch ( UnsupportedEncodingException e ) { + e.printStackTrace( System.err ); + } catch ( java.io.IOException e ) { + e.printStackTrace( System.err ); + } + } + }.start(); + } +} + +// vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/ridljar/com/sun/star/comp/helper/BootstrapException.java b/ridljar/com/sun/star/comp/helper/BootstrapException.java new file mode 100644 index 000000000..11911bbc0 --- /dev/null +++ b/ridljar/com/sun/star/comp/helper/BootstrapException.java @@ -0,0 +1,82 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.helper; + +/** + * BootstrapException is a checked exception that wraps an exception + * thrown by the original target. + * + * @since UDK 3.1.0 + */ +public class BootstrapException extends java.lang.Exception { + + /** + * This field holds the target exception. + */ + private Exception m_target = null; + + /** + * Constructs a <code>BootstrapException</code> with <code>null</code> as + * the target exception. + */ + public BootstrapException() { + super(); + } + + /** + * Constructs a <code>BootstrapException</code> with the specified + * detail message. + * + * @param message the detail message + */ + public BootstrapException( String message ) { + super( message ); + } + + /** + * Constructs a <code>BootstrapException</code> with the specified + * detail message and a target exception. + * + * @param message the detail message + * @param target the target exception + */ + public BootstrapException( String message, Exception target ) { + super( message ); + m_target = target; + } + + /** + * Constructs a <code>BootstrapException</code> with a target exception. + * + * @param target the target exception + */ + public BootstrapException( Exception target ) { + super(); + m_target = target; + } + + /** + * Get the thrown target exception. + * + * @return the target exception + */ + public Exception getTargetException() { + return m_target; + } +} diff --git a/ridljar/com/sun/star/comp/helper/ComponentContext.java b/ridljar/com/sun/star/comp/helper/ComponentContext.java new file mode 100644 index 000000000..34649d563 --- /dev/null +++ b/ridljar/com/sun/star/comp/helper/ComponentContext.java @@ -0,0 +1,308 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.comp.helper; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.Any; + +import com.sun.star.uno.DeploymentException; +import com.sun.star.uno.XComponentContext; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; +import com.sun.star.lang.EventObject; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Map; + + + +class Disposer implements XEventListener +{ + private final XComponent m_xComp; + + + Disposer( XComponent xComp ) + { + m_xComp = xComp; + } + + public void disposing( EventObject Source ) + { + m_xComp.dispose(); + } +} + +/** Component context implementation. +*/ +public class ComponentContext implements XComponentContext, XComponent +{ + private static final boolean DEBUG = false; + private static final String SMGR_NAME = "/singletons/com.sun.star.lang.theServiceManager"; + private static final String TDMGR_NAME = "/singletons/com.sun.star.reflection.theTypeDescriptionManager"; + + private Map<String,Object> m_table; + private XComponentContext m_xDelegate; + + private XMultiComponentFactory m_xSMgr; + private boolean m_bDisposeSMgr; + + private ArrayList<XEventListener> m_eventListener; + + public ComponentContext( Hashtable<String,Object> table, XComponentContext xDelegate ) + { + this((Map<String,Object>) table, xDelegate); + } + + /** Ctor to create a component context passing a hashtable for values and a delegator + reference. Entries of the passed hashtable are either direct values or + ComponentContextEntry objects. + + @param table + entries + @param xDelegate + if values are not found, request is delegated to this object + */ + public ComponentContext( Map<String,Object> table, XComponentContext xDelegate ) + { + m_eventListener = new ArrayList<XEventListener>(); + m_table = table; + m_xDelegate = xDelegate; + m_xSMgr = null; + m_bDisposeSMgr = false; + + Object o = table.get( SMGR_NAME ); + if (o != null) + { + if (o instanceof ComponentContextEntry) + { + o = ((ComponentContextEntry)o).m_value; + } + m_xSMgr = UnoRuntime.queryInterface( + XMultiComponentFactory.class, o ); + } + if (m_xSMgr != null) + { + m_bDisposeSMgr = true; + } + else if (m_xDelegate != null) + { + m_xSMgr = m_xDelegate.getServiceManager(); + } + + // listen for delegate + XComponent xComp = UnoRuntime.queryInterface( + XComponent.class, m_xDelegate ); + if (xComp != null) + { + xComp.addEventListener( new Disposer( this ) ); + } + } + + // XComponentContext impl + + public Object getValueByName( String rName ) + { + Object o = m_table.get( rName ); + if (o == null) + { + if (m_xDelegate != null) + { + return m_xDelegate.getValueByName( rName ); + } + else + { + return Any.VOID; + } + } + + if (!(o instanceof ComponentContextEntry)) + { + // direct value in map + return o; + } + + ComponentContextEntry entry = (ComponentContextEntry)o; + if (entry.m_lateInit == null) + { + return entry.m_value; + } + + Object xInstance = null; + try + { + String serviceName = (String)entry.m_lateInit; + if (serviceName != null) + { + if (m_xSMgr != null) + { + xInstance = m_xSMgr.createInstanceWithContext( serviceName, this ); + } + else + { + if (DEBUG) + System.err.println( "### no service manager instance for late init of singleton instance \"" + rName + "\"!" ); + } + } + else + { + XSingleComponentFactory xCompFac = UnoRuntime.queryInterface( XSingleComponentFactory.class, entry.m_lateInit ); + if (xCompFac != null) + { + xInstance = xCompFac.createInstanceWithContext( this ); + } + else + { + if (DEBUG) + System.err.println( "### neither service name nor service factory given for late init of singleton instance \"" + rName + "\"!" ); + } + } + } + catch (com.sun.star.uno.Exception exc) + { + if (DEBUG) + System.err.println( "### exception occurred on late init of singleton instance \"" + rName + "\": " + exc.getMessage() ); + } + + if (xInstance != null) + { + synchronized (entry) + { + if (entry.m_lateInit != null) + { + entry.m_value = xInstance; + entry.m_lateInit = null; + } + else // inited in the meantime + { + // dispose fresh service instance + XComponent xComp = UnoRuntime.queryInterface( + XComponent.class, xInstance ); + if (xComp != null) + { + xComp.dispose(); + } + } + } + } + else + { + if (DEBUG) + System.err.println( "### failed late init of singleton instance \"" + rName + "\"!" ); + } + return entry.m_value; + } + + public XMultiComponentFactory getServiceManager() + { + if (m_xSMgr == null) + { + throw new DeploymentException( + "null component context service manager" ); + } + return m_xSMgr; + } + + // XComponent impl + + public void dispose() + { + if (DEBUG) + System.err.print( "> disposing context " + this ); + + // fire events + EventObject evt = new EventObject( this ); + for (XEventListener listener : m_eventListener) + { + listener.disposing( evt ); + } + m_eventListener.clear(); + + XComponent tdmgr = null; + // dispose values, then service manager, then typedescription manager + for (Map.Entry<String, Object> entry : m_table.entrySet()) + { + String name = entry.getKey(); + if (! name.equals( SMGR_NAME )) + { + Object o = entry.getValue(); + if (o instanceof ComponentContextEntry) + { + o = ((ComponentContextEntry)o).m_value; + } + + XComponent xComp = UnoRuntime.queryInterface( XComponent.class, o ); + if (xComp != null) + { + if (name.equals( TDMGR_NAME )) + { + tdmgr = xComp; + } + else + { + xComp.dispose(); + } + } + } + } + m_table.clear(); + + // smgr + if (m_bDisposeSMgr) + { + XComponent xComp = UnoRuntime.queryInterface( + XComponent.class, m_xSMgr ); + if (xComp != null) + { + xComp.dispose(); + } + } + m_xSMgr = null; + + // tdmgr + if (tdmgr != null) + { + tdmgr.dispose(); + } + + if (DEBUG) + System.err.println( "... finished" ); + } + + public void addEventListener( XEventListener xListener ) + { + if (xListener == null) + throw new com.sun.star.uno.RuntimeException( "Listener must not be null" ); + if (m_eventListener.contains( xListener )) + throw new com.sun.star.uno.RuntimeException( "Listener already registered." ); + + m_eventListener.add( xListener ); + } + + public void removeEventListener( XEventListener xListener ) + { + if (xListener == null) + throw new com.sun.star.uno.RuntimeException( "Listener must not be null" ); + if (! m_eventListener.contains( xListener )) + throw new com.sun.star.uno.RuntimeException( "Listener is not registered." ); + + m_eventListener.remove( xListener ); + } +} diff --git a/ridljar/com/sun/star/comp/helper/ComponentContextEntry.java b/ridljar/com/sun/star/comp/helper/ComponentContextEntry.java new file mode 100644 index 000000000..aa402d29c --- /dev/null +++ b/ridljar/com/sun/star/comp/helper/ComponentContextEntry.java @@ -0,0 +1,64 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.comp.helper; + +/** Component context entry for constructing ComponentContext objects. + <p> + A ComponentContextEntry is separated into a late-init and direct-value + purpose. + The first one is commonly used for singleton objects of the component + context, that are raised on first-time retrieval of the key. + You have to pass a com.sun.star.lang.XSingleComponentFactory + or string (=> service name) object for this. + </p> +*/ +public class ComponentContextEntry +{ + /** if late init of service instance, set service name (String) or + component factory (XSingleComponentFactory), null otherwise + */ + public Object m_lateInit; + /** set entry value + */ + public Object m_value; + + /** Creating a late-init singleton entry component context entry. + The second parameter will be ignored and overwritten during + instantiation of the singleton instance. + + @param lateInit + object factory or service string + @param value + pass null (dummy separating from second constructor signature) + */ + public ComponentContextEntry( Object lateInit, Object value ) + { + this.m_lateInit = lateInit; + this.m_value = value; + } + /** Creating a direct value component context entry. + + @param value + pass null + */ + public ComponentContextEntry( Object value ) + { + this.m_lateInit = null; + this.m_value = value; + } +} diff --git a/ridljar/com/sun/star/comp/helper/SharedLibraryLoader.java b/ridljar/com/sun/star/comp/helper/SharedLibraryLoader.java new file mode 100644 index 000000000..035644efa --- /dev/null +++ b/ridljar/com/sun/star/comp/helper/SharedLibraryLoader.java @@ -0,0 +1,175 @@ +// -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.comp.helper; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +/** + * @deprecated use class Bootstrap bootstrapping a native UNO installation + * and use the shared library loader service. + * + * The <code>SharedLibraryLoader</code> class provides the functionality of the <code>com.sun.star.loader.SharedLibrary</code> + * service. + * + * See also UNOIDL <code>com.sun.star.lang.ServiceManager</code>. + * + * @see com.sun.star.loader.SharedLibrary + */ +@Deprecated +public class SharedLibraryLoader { + /** + * The default library which contains the SharedLibraryLoader component + */ + public static final String DEFAULT_LIBRARY = "shlibloader.uno"; + + /** + * The default implementation name + */ + public static final String DEFAULT_IMPLEMENTATION = "com.sun.star.comp.stoc.DLLComponentLoader"; + + static { + if ("The Android Project".equals(System.getProperty("java.vendor"))) { + // See corresponding code in + // javaunohelper/com/sun/star/comp/helper/Bootstrap.java for more + // comments. + + boolean disable_dynloading = false; + try { + System.loadLibrary("lo-bootstrap"); + } catch (UnsatisfiedLinkError e) { + disable_dynloading = true; + } + + if (!disable_dynloading) + System.loadLibrary("juh"); + } else + System.loadLibrary("juh"); + } + + private static native boolean component_writeInfo( + String libName, XMultiServiceFactory smgr, XRegistryKey regKey, + ClassLoader loader ); + + private static native Object component_getFactory( + String libName, String implName, XMultiServiceFactory smgr, + XRegistryKey regKey, ClassLoader loader ); + + /** + * Supplies the ServiceFactory of the default SharedLibraryLoader. + * The defaults are "shlibloader.uno" + * for the library and "com.sun.star.comp.stoc.DLLComponentLoader" + * for the component name. + * + * See also UNOIDL <code>com.sun.star.lang.ServiceManager</code> and + * <code>com.sun.star.registry.RegistryKey</code>. + * + * @return the factory for the "com.sun.star.comp.stoc.DLLComponentLoader" component. + * @param smgr the ServiceManager + * @param regKey the root registry key + * @see com.sun.star.loader.SharedLibrary + */ + public static XSingleServiceFactory getServiceFactory( + XMultiServiceFactory smgr, + XRegistryKey regKey ) + { + return UnoRuntime.queryInterface( + XSingleServiceFactory.class, + component_getFactory( + DEFAULT_LIBRARY, DEFAULT_IMPLEMENTATION, smgr, regKey, + SharedLibraryLoader.class.getClassLoader() ) ); + } + + /** + * Loads and returns a specific factory for a given library and implementation name. + * + * See also UNOIDL <code>com.sun.star.lang.ServiceManager</code> and + * <code>com.sun.star.registry.RegistryKey</code>. + * + * @return the factory of the component + * @param libName the name of the shared library + * @param impName the implementation name of the component + * @param smgr the ServiceManager + * @param regKey the root registry key + * @see com.sun.star.loader.SharedLibrary + */ + public static XSingleServiceFactory getServiceFactory( + String libName, + String impName, + XMultiServiceFactory smgr, + XRegistryKey regKey ) + { + return UnoRuntime.queryInterface( + XSingleServiceFactory.class, + component_getFactory( + libName, impName, smgr, regKey, + SharedLibraryLoader.class.getClassLoader() ) ); + } + + /** + * Registers the SharedLibraryLoader under a RegistryKey. + * + * See also UNOIDL <code>com.sun.star.lang.ServiceManager</code> and + * <code>com.sun.star.registry.RegistryKey</code>. + * + * @return true if the registration was successful - otherwise false + * @param smgr the ServiceManager + * @param regKey the root key under that the component should be registered + * @see com.sun.star.loader.SharedLibrary + */ + public static boolean writeRegistryServiceInfo( + com.sun.star.lang.XMultiServiceFactory smgr, + com.sun.star.registry.XRegistryKey regKey ) + { + return component_writeInfo( + DEFAULT_LIBRARY, smgr, regKey, + SharedLibraryLoader.class.getClassLoader() ); + } + + /** + * Registers the SharedLibraryLoader under a RegistryKey. + * + * See also UNOIDL <code>com.sun.star.lang.ServiceManager</code> and + * <code>com.sun.star.registry.RegistryKey</code>. + * + * @return true if the registration was successful - otherwise false + * @param libName name of the shared library + * @param smgr the ServiceManager + * @param regKey the root key under that the component should be registered + * @throws com.sun.star.registry.InvalidRegistryException + * if the registry is not valid. + * + * @see com.sun.star.loader.SharedLibrary + */ + public static boolean writeRegistryServiceInfo( + String libName, + com.sun.star.lang.XMultiServiceFactory smgr, + com.sun.star.registry.XRegistryKey regKey ) + + throws com.sun.star.registry.InvalidRegistryException, + com.sun.star.uno.RuntimeException + { + return component_writeInfo( + libName, smgr, regKey, SharedLibraryLoader.class.getClassLoader() ); + } +} + +// vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/ridljar/com/sun/star/comp/loader/FactoryHelper.java b/ridljar/com/sun/star/comp/loader/FactoryHelper.java new file mode 100644 index 000000000..fe56f594f --- /dev/null +++ b/ridljar/com/sun/star/comp/loader/FactoryHelper.java @@ -0,0 +1,502 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.loader; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +import com.sun.star.uno.XComponentContext; +import com.sun.star.lang.XInitialization; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.registry.XRegistryKey; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.Type; + + +/** + * The purpose of this class to help component implementation. + * + * <p>This class has default implementations for <code>getServiceFactory</code> + * and <code>writeRegistryServiceInfo</code>.</p> + * + * @see com.sun.star.lang.XMultiServiceFactory + * @see com.sun.star.lang.XServiceInfo + * @see com.sun.star.lang.XSingleServiceFactory + * @see com.sun.star.registry.XRegistryKey + * @since UDK1.0 + */ +public class FactoryHelper { + + private static final boolean DEBUG = false; + // the factory + protected static class Factory + implements XSingleServiceFactory, XSingleComponentFactory, XServiceInfo, + XTypeProvider { + + protected XMultiServiceFactory _xMultiServiceFactory; + protected XRegistryKey _xRegistryKey; + protected int _nCode; + protected Constructor<?> _constructor; + protected String _implName; + protected String _serviceName; + + protected Factory(Class<?> implClass, + String serviceName, + XMultiServiceFactory xMultiServiceFactory, + XRegistryKey xRegistryKey) + { + _xMultiServiceFactory = xMultiServiceFactory; + _xRegistryKey = xRegistryKey; + _implName = implClass.getName(); + _serviceName = serviceName; + + Constructor<?> constructors[] = implClass.getConstructors(); + for(int i = 0; i < constructors.length && _constructor == null; ++i) { + Class<?> parameters[] = constructors[i].getParameterTypes(); + + if(parameters.length == 3 + && parameters[0].equals(XComponentContext.class) + && parameters[1].equals(XRegistryKey.class) + && parameters[2].equals(Object[].class)) { + _nCode = 0; + _constructor = constructors[i]; + } + else if(parameters.length == 2 + && parameters[0].equals(XComponentContext.class) + && parameters[1].equals(XRegistryKey.class)) { + _nCode = 1; + _constructor = constructors[i]; + } + else if(parameters.length == 2 + && parameters[0].equals(XComponentContext.class) + && parameters[1].equals(Object[].class)) { + _nCode = 2; + _constructor = constructors[i]; + } + else if(parameters.length == 1 + && parameters[0].equals(XComponentContext.class)) { + _nCode = 3; + _constructor = constructors[i]; + } + // depr + else if(parameters.length == 3 + && parameters[0].equals(XMultiServiceFactory.class) + && parameters[1].equals(XRegistryKey.class) + && parameters[2].equals(Object[].class)) { + _nCode = 4; + _constructor = constructors[i]; + } + else if(parameters.length == 2 + && parameters[0].equals(XMultiServiceFactory.class) + && parameters[1].equals(XRegistryKey.class)) { + _nCode = 5; + _constructor = constructors[i]; + } + else if(parameters.length == 2 + && parameters[0].equals(XMultiServiceFactory.class) + && parameters[1].equals(Object[].class)) { + _nCode = 6; + _constructor = constructors[i]; + } + else if(parameters.length == 1 + && parameters[0].equals(XMultiServiceFactory.class)) { + _nCode = 7; + _constructor = constructors[i]; + } + else if(parameters.length == 1 + && parameters[0].equals(Object[].class)) { + _nCode = 8; + _constructor = constructors[i]; + } + else if(parameters.length == 0) { + _nCode = 9; + _constructor = constructors[i]; + } + } + + if(_constructor == null) // have not found a usable constructor + throw new com.sun.star.uno.RuntimeException(getClass().getName() + " can not find a usable constructor"); + } + + private final XMultiServiceFactory getSMgr( XComponentContext xContext ) + { + if (xContext != null) + { + return UnoRuntime.queryInterface( + XMultiServiceFactory.class, xContext.getServiceManager() ); + } + else + { + return _xMultiServiceFactory; + } + } + + // XComponentContext impl + + public Object createInstanceWithContext( + XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + Object args[]; + switch (_nCode) + { + case 0: + args = new Object [] { xContext, _xRegistryKey, new Object[ 0 ] }; + break; + case 1: + args = new Object [] { xContext, _xRegistryKey }; + break; + case 2: + args = new Object [] { xContext, new Object[ 0 ] }; + break; + case 3: + args = new Object [] { xContext }; + break; + case 4: + args = new Object [] { getSMgr( xContext ), _xRegistryKey, new Object[ 0 ] }; + break; + case 5: + args = new Object [] { getSMgr( xContext ), _xRegistryKey }; + break; + case 6: + args = new Object [] { getSMgr( xContext ), new Object[ 0 ] }; + break; + case 7: + args = new Object [] { getSMgr( xContext ) }; + break; + case 8: + args = new Object [] { new Object[ 0 ] }; + break; + default: + args = new Object [ 0 ]; + break; + } + + try { + return _constructor.newInstance( args ); + } catch (InvocationTargetException invocationTargetException) { + Throwable targetException = invocationTargetException.getCause(); + + if (targetException instanceof java.lang.RuntimeException) + throw (java.lang.RuntimeException)targetException; + else if (targetException instanceof com.sun.star.uno.Exception) + throw (com.sun.star.uno.Exception)targetException; + else if (targetException instanceof com.sun.star.uno.RuntimeException) + throw (com.sun.star.uno.RuntimeException)targetException; + else + throw new com.sun.star.uno.Exception( targetException ); + } catch (IllegalAccessException illegalAccessException) { + throw new com.sun.star.uno.Exception( illegalAccessException ); + } catch (InstantiationException instantiationException) { + throw new com.sun.star.uno.Exception( instantiationException ); + } + } + + public Object createInstanceWithArgumentsAndContext( + Object rArguments[], XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + Object args[]; + + boolean bInitCall = true; + switch (_nCode) + { + case 0: + args = new Object [] { xContext, _xRegistryKey, rArguments }; + bInitCall = false; + break; + case 1: + args = new Object [] { xContext, _xRegistryKey }; + break; + case 2: + args = new Object [] { xContext, rArguments }; + bInitCall = false; + break; + case 3: + args = new Object [] { xContext }; + break; + case 4: + args = new Object [] { getSMgr( xContext ), _xRegistryKey, rArguments }; + bInitCall = false; + break; + case 5: + args = new Object [] { getSMgr( xContext ), _xRegistryKey }; + break; + case 6: + args = new Object [] { getSMgr( xContext ), rArguments }; + bInitCall = false; + break; + case 7: + args = new Object [] { getSMgr( xContext ) }; + break; + case 8: + args = new Object [] { rArguments }; + bInitCall = false; + break; + default: + args = new Object [ 0 ]; + break; + } + + try { + Object instance = _constructor.newInstance( args ); + if (bInitCall) + { + XInitialization xInitialization = UnoRuntime.queryInterface( + XInitialization.class, instance ); + if (xInitialization != null) + { + xInitialization.initialize( rArguments ); + } + } + return instance; + } catch (InvocationTargetException invocationTargetException) { + Throwable targetException = invocationTargetException.getCause(); + + if (targetException instanceof java.lang.RuntimeException) + throw (java.lang.RuntimeException)targetException; + else if (targetException instanceof com.sun.star.uno.Exception) + throw (com.sun.star.uno.Exception)targetException; + else if (targetException instanceof com.sun.star.uno.RuntimeException) + throw (com.sun.star.uno.RuntimeException)targetException; + else + throw new com.sun.star.uno.Exception( targetException ); + } catch (IllegalAccessException illegalAccessException) { + throw new com.sun.star.uno.Exception( illegalAccessException ); + } catch (InstantiationException instantiationException) { + throw new com.sun.star.uno.Exception( instantiationException ); + } + } + + /** + * Creates an instance of the desired service. + * + * @return returns an instance of the desired service. + * @see com.sun.star.lang.XSingleServiceFactory + */ + public Object createInstance() + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + return createInstanceWithContext( null ); + } + + /** + * Creates an instance of the desired service. + * + * @param args the args given to the constructor of the service. + * @return returns an instance of the desired service. + * + * @see com.sun.star.lang.XSingleServiceFactory + */ + public Object createInstanceWithArguments(Object[] args) + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + return createInstanceWithArgumentsAndContext( args, null ); + } + + /** + * Gives the supported services. + * + * @return returns an array of supported services. + * @see com.sun.star.lang.XServiceInfo + */ + public String[] getSupportedServiceNames() throws com.sun.star.uno.RuntimeException { + return new String[]{_serviceName}; + } + + /** + * Gives the implementation name. + * + * @return returns the implementation name. + * @see com.sun.star.lang.XServiceInfo + */ + public String getImplementationName() throws com.sun.star.uno.RuntimeException { + return _implName; + } + + /** + * Indicates if the given service is supported. + * + * @return returns true if the given service is supported. + * @see com.sun.star.lang.XServiceInfo + */ + public boolean supportsService(String serviceName) throws com.sun.star.uno.RuntimeException { + String services[] = getSupportedServiceNames(); + + boolean found = false; + + for(int i = 0; i < services.length && !found; ++i) + found = services[i].equals(serviceName); + + return found; + } + + //XTypeProvider + public byte[] getImplementationId() + { + return new byte[0]; + } + //XTypeProvider + public com.sun.star.uno.Type[] getTypes() + { + Type[] t = new Type[] { + new Type(XSingleServiceFactory.class), + new Type(XSingleComponentFactory.class), + new Type(XServiceInfo.class), + new Type(XTypeProvider.class) + }; + return t; + } + + } + + /** + * Creates a factory for the given class. + * + * @param implClass the implementing class. + * @param multiFactory the given multi service factory (service manager). + * @param regKey the given registry key. + * @return returns a factory. + * + * @see com.sun.star.lang.XServiceInfo + * @deprecated as of UDK 1.0 + */ + @Deprecated + public static XSingleServiceFactory getServiceFactory(Class<?> implClass, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + try { + Field serviceName ; + + try { + serviceName = implClass.getField("__serviceName"); + } catch(NoSuchFieldException noSuchFieldExceptio) { + serviceName = implClass.getField("serviceName"); // old style + } + + xSingleServiceFactory = new Factory(implClass, (String)serviceName.get(null), multiFactory, regKey); + } catch(NoSuchFieldException noSuchFieldException) { + System.err.println("##### FactoryHelper.getServiceFactory - exception:" + noSuchFieldException); + } catch(IllegalAccessException illegalAccessException) { + System.err.println("##### FactoryHelper.getServiceFactory - exception:" + illegalAccessException); + } + + return xSingleServiceFactory; + } + + /** + * Creates a factory for the given class. + * + * @param implClass the implementing class. + * @param serviceName the service name of the implementing class. + * @param multiFactory the given multi service factory (service manager). + * @param regKey the given registry key. + * + * @return returns a factory. + * @see com.sun.star.lang.XServiceInfo + */ + public static XSingleServiceFactory getServiceFactory(Class<?> implClass, + String serviceName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + return new Factory(implClass, serviceName, multiFactory, regKey); + } + + /** + * Creates a factory for the given class. + * + * @param implClass the implementing class. + * @return returns a factory object. + */ + public static Object createComponentFactory( Class<?> implClass, String serviceName ) + { + return new Factory( implClass, serviceName, null, null ); + } + + /** + * Writes the registration data into the registry key. + * + * @param implName the name of the implementing class. + * @param serviceName the service name. + * @param regKey the given registry key. + * @return success. + * + * @see com.sun.star.lang.XServiceInfo + */ + public static boolean writeRegistryServiceInfo(String implName, String serviceName, XRegistryKey regKey) { + boolean result = false; + + try { + XRegistryKey newKey = regKey.createKey("/" + implName + "/UNO/SERVICES"); + + newKey.createKey(serviceName); + + result = true; + } + catch (Exception ex) { + System.err.println(">>>Connection_Impl.writeRegistryServiceInfo " + ex); + } + + return result; + } + + /** + * Writes the registration data into the registry key. + * + * <p>Several services are supported.</p> + * + * @param impl_name name of implementation. + * @param supported_services supported services of implementation. + * @param xKey registry key to write to. + * @return success. + */ + public static boolean writeRegistryServiceInfo( + String impl_name, String supported_services [], XRegistryKey xKey ) + { + try { + XRegistryKey xNewKey = xKey.createKey( "/" + impl_name + "/UNO/SERVICES" ); + for ( int nPos = 0; nPos < supported_services.length; ++nPos ) { + xNewKey.createKey( supported_services[ nPos ] ); + } + return true; + } catch (com.sun.star.registry.InvalidRegistryException exc) { + if (DEBUG) { + System.err.println( + "##### " + Factory.class.getName() + ".writeRegistryServiceInfo -- exc: " + + exc.toString() ); + } + } + return false; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/loader/JavaLoader.java b/ridljar/com/sun/star/comp/loader/JavaLoader.java new file mode 100644 index 000000000..8bc0ae228 --- /dev/null +++ b/ridljar/com/sun/star/comp/loader/JavaLoader.java @@ -0,0 +1,439 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.loader; + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.net.URLDecoder; + +import com.sun.star.loader.CannotActivateFactoryException; +import com.sun.star.loader.XImplementationLoader; + +import com.sun.star.registry.CannotRegisterImplementationException; +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XInitialization; + +import com.sun.star.uno.XComponentContext; +import com.sun.star.beans.XPropertySet; +import com.sun.star.util.XMacroExpander; + +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; + +import com.sun.star.lib.util.StringHelper; + +import com.sun.star.uno.AnyConverter; + + +/** + * The <code>JavaLoader</code> class provides the functionality of the + * <code>com.sun.star.loader.Java</code> service. + * + * <p>Therefore the <code>JavaLoader</code> activates external UNO components + * which are implemented in Java.</p> + * + * <p>The loader is used by the <code>ServiceManger</code>.</p> + * + * @see com.sun.star.loader.XImplementationLoader + * @see com.sun.star.loader.Java + * @see com.sun.star.comp.servicemanager.ServiceManager + * @since UDK1.0 + */ +public class JavaLoader implements XImplementationLoader, + XServiceInfo, + XInitialization +{ + private static final boolean DEBUG = false; + + private static final void DEBUG(String dbg) { + if (DEBUG) System.err.println( dbg ); + } + + private static String[] supportedServices = { + "com.sun.star.loader.Java" + }; + + protected XMultiServiceFactory multiServiceFactory = null; + + private XMacroExpander m_xMacroExpander = null; + private static final String EXPAND_PROTOCOL_PREFIX = "vnd.sun.star.expand:"; + + /** + * Expands macrofied url using the macro expander singleton. + */ + private String expand_url( String url ) throws RuntimeException + { + if (url == null || !url.startsWith( EXPAND_PROTOCOL_PREFIX )) { + return url; + } + try { + if (m_xMacroExpander == null) { + XPropertySet xProps = + UnoRuntime.queryInterface( + XPropertySet.class, multiServiceFactory ); + if (xProps == null) { + throw new com.sun.star.uno.RuntimeException( + "service manager does not support XPropertySet!", + this ); + } + XComponentContext xContext = (XComponentContext) + AnyConverter.toObject( + new Type( XComponentContext.class ), + xProps.getPropertyValue( "DefaultContext" ) ); + m_xMacroExpander = (XMacroExpander)AnyConverter.toObject( + new Type( XMacroExpander.class ), + xContext.getValueByName( + "/singletons/com.sun.star.util.theMacroExpander" ) + ); + } + // decode uric class chars + String macro = URLDecoder.decode( + StringHelper.replace( + url.substring( EXPAND_PROTOCOL_PREFIX.length() ), + '+', "%2B" ), "UTF-8" ); + // expand macro string + String ret = m_xMacroExpander.expandMacros( macro ); + if (DEBUG) { + System.err.println( + "JavaLoader.expand_url(): " + url + " => " + + macro + " => " + ret ); + } + return ret; + } catch (com.sun.star.uno.Exception exc) { + throw new com.sun.star.uno.RuntimeException(exc, + exc.getMessage(), this ); + } catch (java.lang.Exception exc) { + throw new com.sun.star.uno.RuntimeException(exc, + exc.getMessage(), this ); + } + } + + /** + * Default constructor. + * + * <p>Creates a new instance of the <code>JavaLoader</code> class.</p> + */ + public JavaLoader() {} + + /** + * Creates a new <code>JavaLoader</code> object. + * + * <p>The specified <code>com.sun.star.lang.XMultiServiceFactory</code> is + * the <code>ServiceManager</code> service which can be delivered to all + * components the <code>JavaLoader</code> is loading.</p> + * + * <p>To set the <code>MultiServiceFactory</code> you can use the + * <code>com.sun.star.lang.XInitialization</code> interface, either.</p> + * + * @param factory the <code>ServiceManager</code>. + * @see com.sun.star.comp.servicemanager.ServiceManager + * @see com.sun.star.lang.XInitialization + */ + public JavaLoader(XMultiServiceFactory factory) { + multiServiceFactory = factory; + } + + /** + * Unlike the original intention, the method could be called every time a + * new <code>com.sun.star.lang.XMultiServiceFactory</code> should be set at + * the loader. + * + * @param args - the first parameter (args[0]) specifies the <code>ServiceManager</code>. + * @see com.sun.star.lang.XInitialization + * @see com.sun.star.comp.servicemanager.ServiceManager + */ + public void initialize( java.lang.Object[] args ) + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + if (args.length == 0) + throw new com.sun.star.lang.IllegalArgumentException("No arguments specified"); + + try { + multiServiceFactory = (XMultiServiceFactory) AnyConverter.toObject( + new Type(XMultiServiceFactory.class), args[0]); + } catch (ClassCastException castEx) { + throw new com.sun.star.lang.IllegalArgumentException(castEx, + "The argument must be an instance of XMultiServiceFactory"); + } + } + + /** + * Supplies the implementation name of the component. + * + * @return the implementation name - here the class name. + * @see com.sun.star.lang.XServiceInfo + */ + public String getImplementationName() + throws com.sun.star.uno.RuntimeException + { + return getClass().getName(); + } + + /** + * Verifies if a given service is supported by the component. + * + * @param serviceName the name of the service that should be checked. + * @return true,if service is supported - otherwise false. + * + * @see com.sun.star.lang.XServiceInfo + */ + public boolean supportsService(String serviceName) + throws com.sun.star.uno.RuntimeException + { + for (String supportedService : supportedServices) { + if (supportedService.equals(serviceName)) { + return true; + } + } + return false; + } + + /** + * Supplies a list of all service names supported by the component. + * + * @return a String array with all supported services. + * @see com.sun.star.lang.XServiceInfo + */ + public String[] getSupportedServiceNames() + throws com.sun.star.uno.RuntimeException + { + return supportedServices; + } + + /** + * Provides a components factory. + * + * <p>The <code>JavaLoader</code> tries to load the class first. If a + * location URL is given the RegistrationClassFinder is used to load the + * class. Otherwise the class is loaded through the Class.forName method.</p> + * + * <p>To get the factory the inspects the class for the optional static member + * functions __getServiceFactory resp. getServiceFactory (DEPRECATED).</p> + * + * @param implementationName the implementation (class) name of the component. + * @param implementationLoaderUrl the URL of the implementation loader. Not used. + * @param locationUrl points to an archive (JAR file) which contains a component. + * @param xKey registry key. + * @return the factory for the component (@see com.sun.star.lang.XSingleServiceFactory) + * + * @see com.sun.star.loader.XImplementationLoader + * @see com.sun.star.comp.loader.RegistrationClassFinder + */ + public java.lang.Object activate( String implementationName, + String implementationLoaderUrl, + String locationUrl, + XRegistryKey xKey ) + throws CannotActivateFactoryException, + com.sun.star.uno.RuntimeException + { + locationUrl = expand_url( locationUrl ); + + Object returnObject = null; + Class<?> clazz ; + + DEBUG("try to get factory for " + implementationName); + + // First we must get the class of the implementation + // 1. If a location URL is given it is assumed that this points to a JAR file. + // The components class name is stored in the manifest file. + // 2. If only the implementation name is given, the class is loaded with the + // Class.forName() method. This is a hack to load bootstrap components. + // Normally a string must no be null. + try { + if ( locationUrl != null ) { + clazz = RegistrationClassFinder.find( locationUrl ); + if (clazz == null) { + throw new CannotActivateFactoryException( + "Cannot activate jar " + locationUrl); + } + } else { + clazz = Class.forName( implementationName ); + if (clazz == null) { + throw new CannotActivateFactoryException( + "Cannot find class " + implementationName); + } + } + } catch (java.net.MalformedURLException e) { + throw new CannotActivateFactoryException(e, "Can not activate factory because " + e); + } catch (java.io.IOException e) { + throw new CannotActivateFactoryException(e, "Can not activate factory because " + e); + } catch (java.lang.ClassNotFoundException e) { + throw new CannotActivateFactoryException(e, "Can not activate factory because " + e); + } + + Class<?>[] paramTypes = {String.class, XMultiServiceFactory.class, XRegistryKey.class}; + Object[] params = { implementationName, multiServiceFactory, xKey }; + + // try to get factory from implementation class + // latest style: use the public static method __getComponentFactory + // - new style: use the public static method __getServiceFactory + // - old style: use the public static method getServiceFactory ( DEPRECATED ) + + Method compfac_method = null; + try { + compfac_method = clazz.getMethod( + "__getComponentFactory", new Class [] { String.class } ); + } catch ( NoSuchMethodException noSuchMethodEx) { + } catch ( SecurityException secEx) { + } + + Method method = null; + if (null == compfac_method) { + try { + method = clazz.getMethod("__getServiceFactory", paramTypes); + } catch ( NoSuchMethodException noSuchMethodEx) { + } catch ( SecurityException secEx) { + } + } + + try { + if (null != compfac_method) { + Object ret = compfac_method.invoke( clazz, new Object [] { implementationName } ); + if (!(ret instanceof XSingleComponentFactory)) + throw new CannotActivateFactoryException( + "No factory object for " + implementationName ); + + return ret; + } + else { + if ( method == null ) + method = clazz.getMethod("getServiceFactory", paramTypes); + + Object oRet = method.invoke(clazz, params); + + if ( (oRet != null) && (oRet instanceof XSingleServiceFactory) ) + returnObject = oRet; + } + } catch ( NoSuchMethodException e) { + throw new CannotActivateFactoryException(e, "Can not activate the factory for " + + implementationName); + } catch ( SecurityException e) { + throw new CannotActivateFactoryException(e, "Can not activate the factory for " + + implementationName); + } catch ( IllegalAccessException e ) { + throw new CannotActivateFactoryException(e, "Can not activate the factory for " + + implementationName); + } + catch ( IllegalArgumentException e ) { + throw new CannotActivateFactoryException(e, "Can not activate the factory for " + + implementationName); + } catch ( InvocationTargetException e ) { + throw new CannotActivateFactoryException(e, "Can not activate the factory for " + + implementationName); + } + + return returnObject; + } + + /** + * Registers the component in a registry under a given root key. + * + * <p>If the component supports the optional + * methods __writeRegistryServiceInfo, writeRegistryServiceInfo (DEPRECATED), + * the call is delegated to that method. Otherwise a default registration + * will be accomplished.</p> + * + * @param regKey the root key under that the component should be registered. + * @param implementationLoaderUrl specifies the loader, the component is loaded by. + * @param locationUrl points to an archive (JAR file) which contains a component. + * @return true if registration is successfully - otherwise false. + */ + public boolean writeRegistryInfo( XRegistryKey regKey, + String implementationLoaderUrl, + String locationUrl ) + throws CannotRegisterImplementationException, + com.sun.star.uno.RuntimeException + { + locationUrl = expand_url( locationUrl ); + + boolean success = false; + + try { + + Class<?> clazz = RegistrationClassFinder.find(locationUrl); + if (null == clazz) + throw new CannotRegisterImplementationException( + "Cannot determine registration class!" ); + + Class<?>[] paramTypes = { XRegistryKey.class }; + Object[] params = { regKey }; + + Method method = clazz.getMethod("__writeRegistryServiceInfo", paramTypes); + Object oRet = method.invoke(clazz, params); + + if ( (oRet != null) && (oRet instanceof Boolean) ) + success = ((Boolean) oRet).booleanValue(); + } catch (Exception e) { + throw new CannotRegisterImplementationException(e, e.toString()); + } + + return success; + } + + /** + * Supplies the factory for the <code>JavaLoader</code>. + * + * @param implName the name of the desired component. + * @param multiFactory the <code>ServiceManager</code> is delivered to the factory. + * @param regKey not used - can be null. + * @return the factory for the <code>JavaLoader</code>. + */ + public static XSingleServiceFactory getServiceFactory( String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + if ( implName.equals(JavaLoader.class.getName()) ) + return new JavaLoaderFactory( multiFactory ); + + return null; + } + + /** + * Registers the <code>JavaLoader</code> at the registry. + * + * @param regKey root key under which the <code>JavaLoader</code> should be registered. + * @return true if registration succeeded - otherwise false. + */ + public static boolean writeRegistryServiceInfo(XRegistryKey regKey) { + boolean result = false; + + try { + XRegistryKey newKey = regKey.createKey("/" + JavaLoader.class.getName() + "/UNO/SERVICE"); + + for (String supportedService : supportedServices) { + newKey.createKey(supportedService); + } + + result = true; + } catch (Exception ex) { + if (DEBUG) System.err.println(">>>JavaLoader.writeRegistryServiceInfo " + ex); + } + + return result; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/loader/JavaLoaderFactory.java b/ridljar/com/sun/star/comp/loader/JavaLoaderFactory.java new file mode 100644 index 000000000..edf4f7a3a --- /dev/null +++ b/ridljar/com/sun/star/comp/loader/JavaLoaderFactory.java @@ -0,0 +1,90 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.loader; + +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; + + +public class JavaLoaderFactory implements XSingleServiceFactory, XServiceInfo { + + private static String[] supportedServices = { + "com.sun.star.loader.Java", + "com.sun.star.loader.Java2" + }; + + protected XMultiServiceFactory multiServiceFactory = null; + + public JavaLoaderFactory(XMultiServiceFactory factory) { + multiServiceFactory = factory; + } + + public java.lang.Object createInstance() + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + return new JavaLoader(multiServiceFactory); + } + + public java.lang.Object createInstanceWithArguments( java.lang.Object[] args ) + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + JavaLoader loader = new JavaLoader(); + loader.initialize(args); + + return loader; + } + + /** + * Implements the XServiceInfo interface. + */ + public String getImplementationName() + throws com.sun.star.uno.RuntimeException + { + return JavaLoader.class.getName(); + } + + /** + * Implements the XServiceInfo interface. + */ + public boolean supportsService(String serviceName) + throws com.sun.star.uno.RuntimeException + { + for (String supportedService : supportedServices) { + if (supportedService.equals(serviceName)) { + return true; + } + } + return false; + } + + /** + * Implements the XServiceInfo interface. + */ + public String[] getSupportedServiceNames() + throws com.sun.star.uno.RuntimeException + { + return supportedServices; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/loader/RegistrationClassFinder.java b/ridljar/com/sun/star/comp/loader/RegistrationClassFinder.java new file mode 100644 index 000000000..e7679da16 --- /dev/null +++ b/ridljar/com/sun/star/comp/loader/RegistrationClassFinder.java @@ -0,0 +1,69 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.loader; + +import com.sun.star.lib.unoloader.UnoClassLoader; +import com.sun.star.lib.util.WeakMap; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.jar.Attributes; + +final class RegistrationClassFinder { + public static Class<?> find(String locationUrl) + throws ClassNotFoundException, IOException + { + synchronized (map) { + Class<?> c = (Class<?>) WeakMap.getValue(map.get(locationUrl)); + if (c != null) { + return c; + } + } + URL url = new URL(locationUrl); + Attributes attr = UnoClassLoader.getJarMainAttributes(url); + String name = attr == null + ? null : attr.getValue("RegistrationClassName"); + if (name == null) { + return null; + } + ClassLoader cl1 = RegistrationClassFinder.class.getClassLoader(); + ClassLoader cl2; + if (cl1 instanceof UnoClassLoader) { + cl2 = ((UnoClassLoader) cl1).getClassLoader(url, attr); + } else { + cl2 = URLClassLoader.newInstance(new URL[] { url }, cl1); + } + Class<?> c = cl2.loadClass(name); + synchronized (map) { + Class<?> c2 = (Class<?>) WeakMap.getValue(map.get(locationUrl)); + if (c2 != null) { + return c2; + } + map.put(locationUrl, c); + } + return c; + } + + private RegistrationClassFinder() {} // do not instantiate + + private static final WeakMap map = new WeakMap(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/servicemanager/ServiceManager.java b/ridljar/com/sun/star/comp/servicemanager/ServiceManager.java new file mode 100644 index 000000000..574983f1e --- /dev/null +++ b/ridljar/com/sun/star/comp/servicemanager/ServiceManager.java @@ -0,0 +1,662 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.servicemanager; + +import java.util.ArrayList; +import java.util.Collections; + +import com.sun.star.container.XContentEnumerationAccess; +import com.sun.star.container.XEnumeration; +import com.sun.star.container.XSet; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +/** + * The <code>ServiceManager</code> class is an implementation of the + * <code>ServiceManager</code>the central class needed for implementing or using + * UNO components in Java. + * + * <p>The Methods <code>queryInterface</code> and <code>isSame</code> delegate + * calls to the implementing objects and are used instead of casts and identity + * comparisons.</p> + * + * @see com.sun.star.lang.XMultiServiceFactory + * @see com.sun.star.container.XSet + * @see com.sun.star.container.XContentEnumerationAccess + * @see com.sun.star.lang.XComponent + * @see com.sun.star.lang.XServiceInfo + * @since UDK1.0 + */ +public class ServiceManager implements XMultiServiceFactory, + XMultiComponentFactory, + XSet, + XContentEnumerationAccess, + XComponent, + XServiceInfo +{ + private static final boolean DEBUG = false; + + private static final void DEBUG (String dbg) { + if (DEBUG) System.err.println( dbg ); + } + + private static com.sun.star.uno.Type UNO_TYPE = null; + + static String[] supportedServiceNames = { + "com.sun.star.lang.MultiServiceFactory", + "com.sun.star.lang.ServiceManager" + }; + + ArrayList<XEventListener> eventListener; + java.util.HashMap<String, Object> factoriesByImplNames; + java.util.HashMap<String, ArrayList<Object>> factoriesByServiceNames; // keys: + + private com.sun.star.uno.XComponentContext m_xDefaultContext; + + /** + * Creates a new instance of the <code>ServiceManager</code>. + */ + public ServiceManager() { + eventListener = new ArrayList<XEventListener>(); + factoriesByImplNames = new java.util.HashMap<String, Object>(); + factoriesByServiceNames = new java.util.HashMap<String, ArrayList<Object>>(); + m_xDefaultContext = null; + } + + public void setDefaultContext(XComponentContext context) { + m_xDefaultContext = context; + } + + /** + * Creates a new instance of a specified service. + * + * <p>Therefore the associated factory of the service is looked up and used + * to instantiate a new component. </p> + * + * @param serviceSpecifier indicates the service or component name. + * @return newly created component. + * + * @see com.sun.star.lang.XMultiServiceFactory + */ + public java.lang.Object createInstance( String serviceSpecifier ) + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + return createInstanceWithContext( serviceSpecifier, m_xDefaultContext ); + } + + /** + * Creates a new instance of a specified service with the given parameters. + * + * <p>Therefore the associated factory of the service is looked up and used + * to instantiate a new component.</p> + * + * @return newly created component. + * @param serviceSpecifier indicates the service or component name. + * @see com.sun.star.lang.XMultiServiceFactory + */ + public java.lang.Object createInstanceWithArguments( + String serviceSpecifier, Object[] args ) + throws com.sun.star.uno.Exception, com.sun.star.uno.RuntimeException + { + if (DEBUG) { + System.err.println("createInstanceWithArguments:" ); + + for (Object arg : args) { + System.err.print(" " + arg); + } + + System.err.println(); + } + + return createInstanceWithArgumentsAndContext( serviceSpecifier, args, m_xDefaultContext ); + } + + /** + * Look up the factory for a given service or implementation name. + * + * <p>First the requested service name is search in the list of available + * services. If it can not be found the name is looked up in the implementation + * list.</p> + * + * @param serviceName indicates the service or implementation name. + * @return the factory of the service / implementation. + * + * @see com.sun.star.lang.XMultiServiceFactory + */ + private Object queryServiceFactory(String serviceName) + throws com.sun.star.uno.Exception, + com.sun.star.uno.RuntimeException + { + DEBUG("queryServiceFactory for name " + serviceName ); + Object factory = null; + + if ( factoriesByServiceNames.containsKey( serviceName ) ) { + ArrayList<Object> availableFact = factoriesByServiceNames.get( serviceName ); + + DEBUG(""); + DEBUG("available factories for " + serviceName +" "+ availableFact); + DEBUG(""); + + if ( !availableFact.isEmpty() ) + factory = availableFact.get(availableFact.size()-1); + + } else // not found in list of services - now try the implementations + factory = factoriesByImplNames.get( serviceName ); // return null if none is available + + if (DEBUG) { + if (factory == null) System.err.println("service not registered"); + else + System.err.println("service found:" + factory + " " + UnoRuntime.queryInterface(XSingleServiceFactory.class, factory)); + } + + if (factory == null) + throw new com.sun.star.uno.Exception("Query for service factory for " + serviceName + " failed."); + + return factory; + } + + /** + * Supplies a list of all available services names. + * + * @return list of Strings of all service names. + * @see com.sun.star.container.XContentEnumerationAccess + */ + public String[] getAvailableServiceNames() + throws com.sun.star.uno.RuntimeException + { + try{ + return factoriesByServiceNames.keySet().toArray( + new String[ factoriesByServiceNames.size() ] ); + } catch(Exception ex) { + throw new com.sun.star.uno.RuntimeException(ex); + } + } + + // XMultiComponentFactory implementation + + /** Create a service instance with given context. + * + * @param rServiceSpecifier service name. + * @param xContext context. + * @return service instance. + */ + public java.lang.Object createInstanceWithContext( + String rServiceSpecifier, + com.sun.star.uno.XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + Object fac = queryServiceFactory( rServiceSpecifier ); + if (fac != null) + { + XSingleComponentFactory xCompFac = UnoRuntime.queryInterface( + XSingleComponentFactory.class, fac ); + if (xCompFac != null) + { + return xCompFac.createInstanceWithContext( xContext ); + } + else + { + XSingleServiceFactory xServiceFac = UnoRuntime.queryInterface( + XSingleServiceFactory.class, fac ); + if (xServiceFac != null) + { + if (DEBUG) + System.err.println( "### ignoring context raising service \"" + rServiceSpecifier + "\"!" ); + return xServiceFac.createInstance(); + } + else + { + throw new com.sun.star.uno.Exception( + "retrieved service factory object for \"" + rServiceSpecifier + + "\" does not export XSingleComponentFactory nor XSingleServiceFactory!" ); + } + } + } + return null; + } + /** + * Create a service instance with given context and arguments. + * + * @param rServiceSpecifier service name. + * @param rArguments arguments. + * @param xContext context. + * @return service instance. + */ + public java.lang.Object createInstanceWithArgumentsAndContext( + String rServiceSpecifier, + java.lang.Object[] rArguments, + com.sun.star.uno.XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + Object fac = queryServiceFactory( rServiceSpecifier ); + if (fac != null) + { + XSingleComponentFactory xCompFac = UnoRuntime.queryInterface( + XSingleComponentFactory.class, fac ); + if (xCompFac != null) + { + return xCompFac.createInstanceWithArgumentsAndContext( rArguments, xContext ); + } + else + { + XSingleServiceFactory xServiceFac = UnoRuntime.queryInterface( + XSingleServiceFactory.class, fac ); + if (xServiceFac != null) + { + if (DEBUG) + System.err.println( "### ignoring context raising service \"" + rServiceSpecifier + "\"!" ); + return xServiceFac.createInstanceWithArguments( rArguments ); + } + else + { + throw new com.sun.star.uno.Exception( + "retrieved service factory object for \"" + rServiceSpecifier + + "\" does not export XSingleComponentFactory nor XSingleServiceFactory!" ); + } + } + } + return null; + } + + /** + * Removes all listeners from the <code>ServiceManager</code> and clears the + * list of the services. + * + * @see com.sun.star.lang.XComponent + */ + public void dispose() + throws com.sun.star.uno.RuntimeException + { + if (eventListener != null) { + for (XEventListener listener : eventListener) { + listener.disposing(new com.sun.star.lang.EventObject(this)); + } + eventListener.clear(); + } + + factoriesByServiceNames.clear(); + factoriesByImplNames.clear(); + } + + /** + * Adds a new <code>EventListener</code>. + * + * <p>The listener is notified when a service is added (removed) to (from) + * the <code>ServiceManager</code>.</p> + * + * <p>If the listener is already registered a + * <code>com.sun.star.uno.RuntimeException</code> will be thrown.</p> + * + * @param xListener the new listener which should been added. + * @see com.sun.star.lang.XComponent + */ + public void addEventListener( XEventListener xListener ) + throws com.sun.star.uno.RuntimeException + { + if (xListener == null) + throw new com.sun.star.uno.RuntimeException("Listener must not be null"); + + if ( eventListener.contains(xListener) ) + throw new com.sun.star.uno.RuntimeException("Listener already registered."); + + eventListener.add(xListener); + } + + /** + * Removes a <code>EventListener</code> from the <code>ServiceManager</code>. + * + * <p>If the listener is not registered a <code>com.sun.star.uno.RuntimeException</code> + * will be thrown.</p> + * + * @param xListener the new listener which should been removed. + * @see com.sun.star.lang.XComponent + */ + public void removeEventListener( XEventListener xListener ) + throws com.sun.star.uno.RuntimeException + { + if (xListener == null) + throw new com.sun.star.uno.RuntimeException("Listener must not be null"); + + if ( !eventListener.contains(xListener) ) + throw new com.sun.star.uno.RuntimeException("Listener is not registered."); + + eventListener.remove(xListener); + } + + /** + * Checks if a component is registered at the <code>ServiceManager</code>. + * + * <p>The given object argument must provide a <code>XServiceInfo</code> + * interface.</p> + * + * @param object object which provides a <code>XServiceInfo</code> interface. + * @return true if the component is registered otherwise false. + * + * @see com.sun.star.container.XSet + * @see com.sun.star.lang.XServiceInfo + */ + public boolean has( Object object ) + throws com.sun.star.uno.RuntimeException + { + if (object == null) + throw new com.sun.star.uno.RuntimeException("The parameter must not been null"); + + XServiceInfo xServiceInfo = UnoRuntime.queryInterface(XServiceInfo.class, object); + + return xServiceInfo != null && UnoRuntime.areSame(factoriesByImplNames.get(xServiceInfo.getImplementationName()), object); + } + + /** + * Adds a <code>SingleServiceFactory</code> to the <code>ServiceManager</code>. + * + * @param object factory which should be added. + * @see com.sun.star.container.XSet + * @see com.sun.star.lang.XSingleServiceFactory + */ + public void insert( Object object ) + throws com.sun.star.lang.IllegalArgumentException, + com.sun.star.container.ElementExistException, + com.sun.star.uno.RuntimeException + { + if (object == null) throw new com.sun.star.lang.IllegalArgumentException(); + + XServiceInfo xServiceInfo = + UnoRuntime.queryInterface(XServiceInfo.class, object); + + if (xServiceInfo == null) + throw new com.sun.star.lang.IllegalArgumentException( + "The given object does not implement the XServiceInfo interface." + ); + + if ( factoriesByImplNames.containsKey( xServiceInfo.getImplementationName() ) ) { + throw new com.sun.star.container.ElementExistException( + xServiceInfo.getImplementationName() + " already registered" + ); + } + + DEBUG("add factory " + object.toString() + " for " + xServiceInfo.getImplementationName()); + factoriesByImplNames.put( xServiceInfo.getImplementationName(), object ); + + + String[] serviceNames = xServiceInfo.getSupportedServiceNames(); + ArrayList<Object> vec ; + + for (String serviceName : serviceNames) { + if (!factoriesByServiceNames.containsKey(serviceName)) { + DEBUG("> no registered services found under " + serviceName + ": adding..."); + factoriesByServiceNames.put(serviceName, new ArrayList<Object>()); + } + vec = factoriesByServiceNames.get(serviceName); + if (vec.contains( object )) { + System.err.println("The implementation " + xServiceInfo.getImplementationName() + + " already registered for the service " + serviceName + " - ignoring!"); + } else { + vec.add(object); + } + } + } + + /** + * Removes a <code>SingleServiceFactory</code> from the <code>ServiceManager</code>. + * + * @param object factory which should be removed. + * @see com.sun.star.container.XSet + * @see com.sun.star.lang.XSingleServiceFactory + */ + public void remove( Object object ) + throws com.sun.star.lang.IllegalArgumentException, + com.sun.star.container.NoSuchElementException, + com.sun.star.uno.RuntimeException + { + if (object == null) + throw new com.sun.star.lang.IllegalArgumentException( + "The given object must not be null." + ); + + XServiceInfo xServiceInfo = + UnoRuntime.queryInterface(XServiceInfo.class, object); + + if (xServiceInfo == null) + throw new com.sun.star.lang.IllegalArgumentException( + "The given object does not implement the XServiceInfo interface." + ); + + XSingleServiceFactory xSingleServiceFactory = + UnoRuntime.queryInterface(XSingleServiceFactory.class, object); + + if (xSingleServiceFactory == null) + throw new com.sun.star.lang.IllegalArgumentException( + "The given object does not implement the XSingleServiceFactory interface." + ); + + if ( factoriesByImplNames.remove( xServiceInfo.getImplementationName() ) == null ) + throw new com.sun.star.container.NoSuchElementException( + xServiceInfo.getImplementationName() + + " is not registered as an implementation." + ); + + String[] serviceNames = xServiceInfo.getSupportedServiceNames(); + + for (String serviceName : serviceNames) { + if (factoriesByServiceNames.containsKey(serviceName)) { + ArrayList<Object> vec = factoriesByServiceNames.get(serviceName); + if (!vec.remove(object)) { + System.err.println("The implementation " + xServiceInfo.getImplementationName() + + " is not registered for the service " + serviceName + " - ignoring!"); + } + // remove the vector if no implementations available for the service + if (vec.isEmpty()) { + factoriesByServiceNames.remove(serviceName); + } + } + } + } + + /** + * Provides an enumeration of all registered services. + * + * @return an enumeration of all available services. + * @see com.sun.star.container.XEnumerationAccess + */ + public XEnumeration createEnumeration() + throws com.sun.star.uno.RuntimeException + { + return new ServiceEnumerationImpl( factoriesByImplNames.values().iterator() ); + } + + /** + * Provides the UNO type of the <code>ServiceManager</code> + * + * @return the UNO type of the <code>ServiceManager</code>. + * @see com.sun.star.container.XElementAccess + * @see com.sun.star.uno.TypeClass + */ + public com.sun.star.uno.Type getElementType() + throws com.sun.star.uno.RuntimeException + { + if ( UNO_TYPE == null ) + UNO_TYPE = new com.sun.star.uno.Type(ServiceManager.class); + + return UNO_TYPE; + } + + /** + * Checks if the any components are registered. + * + * @return true - if the list of the registered components is not empty - otherwise false. + * @see com.sun.star.container.XElementAccess + */ + public boolean hasElements() { + return ! factoriesByImplNames.isEmpty(); + } + + /** + * Provides an enumeration of all factories for a specified service. + * + * @param serviceName name of the requested service. + * @return an enumeration for service name. + * @see com.sun.star.container.XContentEnumerationAccess + */ + public XEnumeration createContentEnumeration( String serviceName ) + throws com.sun.star.uno.RuntimeException + { + XEnumeration enumer ; + + ArrayList<Object> serviceList = factoriesByServiceNames.get(serviceName); + + if (serviceList != null) + enumer = new ServiceEnumerationImpl( serviceList.iterator() ); + else + enumer = new ServiceEnumerationImpl(); + + return enumer; + } + + /** + * Returns the implementation name of the <code>ServiceManager</code> component. + * + * @return the class name of the <code>ServiceManager</code>. + * @see com.sun.star.lang.XServiceInfo + */ + public String getImplementationName() + throws com.sun.star.uno.RuntimeException + { + return getClass().getName(); + } + + /** + * Checks if the <code>ServiceManager</code> supports a service. + * + * @param serviceName service name which should be checked. + * @return true if the service is supported - otherwise false. + * + * @see com.sun.star.lang.XServiceInfo + */ + public boolean supportsService( String serviceName ) + throws com.sun.star.uno.RuntimeException + { + for (String supportedServiceName : supportedServiceNames) { + if (supportedServiceName.equals(serviceName)) { + return true; + } + } + + return getImplementationName().equals(serviceName); + } + + /** + * Supplies list of all supported services. + * + * @return a list of all supported service names. + * @see com.sun.star.lang.XServiceInfo + */ + public String[] getSupportedServiceNames() + throws com.sun.star.uno.RuntimeException + { + return supportedServiceNames; + } + + /** + * The <code>ServiceEnumerationImpl</code> class provides an + * implementation of the @see com.sun.star.container.XEnumeration interface. + * + * <p>It is an inner wrapper for a java.util.Enumeration object.</p> + * + * @see com.sun.star.lang.XSingleServiceFactory + * @see com.sun.star.lang.XServiceInfo + * @since UDK1.0 + */ + static class ServiceEnumerationImpl implements XEnumeration { + java.util.Iterator<Object> enumeration = null; + + /** + * Constructs a new empty instance. + */ + public ServiceEnumerationImpl() { + } + + /** + * Constructs a new instance with a given enumeration. + * + * @param enumer is the enumeration which should been wrapped. + * @see com.sun.star.container.XEnumeration + */ + public ServiceEnumerationImpl(java.util.Enumeration<Object> enumer) { + enumeration = Collections.list(enumer).iterator(); + } + + /** + * Constructs a new instance with a given enumeration. + * + * @param enumer is the enumeration which should been wrapped. + * @see com.sun.star.container.XEnumeration + */ + public ServiceEnumerationImpl(java.util.Iterator<Object> enumer) { + enumeration = enumer; + } + + /** + * Checks if the enumeration contains more elements. + * + * @return true if more elements are available - otherwise false. + * @see com.sun.star.container.XEnumeration + */ + public boolean hasMoreElements() + throws com.sun.star.uno.RuntimeException + { + return enumeration != null && enumeration.hasNext(); + + } + + /** + * Returns the next element of the enumeration. + * + * <p>If no further elements available a com.sun.star.container.NoSuchElementException + * exception will be thrown.</p> + * + * @return the next element. + * @see com.sun.star.container.XEnumeration + */ + public Object nextElement() + throws com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException, + com.sun.star.uno.RuntimeException + { + if (enumeration == null) + throw new com.sun.star.container.NoSuchElementException(); + + try { + return enumeration.next(); + } catch (java.util.NoSuchElementException e) { + throw new com.sun.star.container.NoSuchElementException(e, e.toString()); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/comp/urlresolver/UrlResolver.java b/ridljar/com/sun/star/comp/urlresolver/UrlResolver.java new file mode 100644 index 000000000..7585dabf6 --- /dev/null +++ b/ridljar/com/sun/star/comp/urlresolver/UrlResolver.java @@ -0,0 +1,147 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.comp.urlresolver; + + +import com.sun.star.bridge.XBridge; +import com.sun.star.bridge.XBridgeFactory; +import com.sun.star.bridge.XUnoUrlResolver; + +import com.sun.star.comp.loader.FactoryHelper; + +import com.sun.star.connection.ConnectionSetupException; +import com.sun.star.connection.NoConnectException; +import com.sun.star.connection.XConnection; +import com.sun.star.connection.XConnector; + +import com.sun.star.lang.IllegalArgumentException; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.uno.UnoRuntime; + + +/** + * This component gives a factory for an <code>UnoUrlResolver</code> service. + * + * @see com.sun.star.bridge.XBridgeFactory + * @see com.sun.star.connection.Connector + * @since UDK1.0 + */ +public class UrlResolver { + public static class _UrlResolver implements XUnoUrlResolver { + private static final String __serviceName = "com.sun.star.bridge.UnoUrlResolver"; + + private final XMultiServiceFactory _xMultiServiceFactory; + + public _UrlResolver(XMultiServiceFactory xMultiServiceFactory) { + _xMultiServiceFactory = xMultiServiceFactory; + } + + public Object resolve(/*IN*/String dcp) throws NoConnectException, ConnectionSetupException, IllegalArgumentException, com.sun.star.uno.RuntimeException { + String conDcp ; + String protDcp ; + String rootOid ; + + if(dcp.indexOf(';') == -1) {// use old style + conDcp = dcp; + protDcp = "iiop"; + rootOid = "classic_uno"; + } + else { // new style + int index = dcp.indexOf(':'); + dcp = dcp.substring(index + 1).trim(); + + index = dcp.indexOf(';'); + conDcp = dcp.substring(0, index).trim(); + dcp = dcp.substring(index + 1).trim(); + + index = dcp.indexOf(';'); + protDcp = dcp.substring(0, index).trim(); + dcp = dcp.substring(index + 1).trim(); + + rootOid = dcp.trim().trim(); + } + + Object rootObject ; + XBridgeFactory xBridgeFactory ; + try { + xBridgeFactory = UnoRuntime.queryInterface(XBridgeFactory.class, + _xMultiServiceFactory.createInstance("com.sun.star.bridge.BridgeFactory")); + } catch (com.sun.star.uno.Exception e) { + throw new com.sun.star.uno.RuntimeException(e); + } + XBridge xBridge = xBridgeFactory.getBridge(conDcp + ";" + protDcp); + + if(xBridge == null) { + Object connector ; + try { + connector = _xMultiServiceFactory.createInstance("com.sun.star.connection.Connector"); + } catch (com.sun.star.uno.Exception e) { + throw new com.sun.star.uno.RuntimeException(e); + } + + XConnector connector_xConnector = UnoRuntime.queryInterface(XConnector.class, connector); + + // connect to the server + XConnection xConnection = connector_xConnector.connect(conDcp); + try { + xBridge = xBridgeFactory.createBridge(conDcp + ";" + protDcp, protDcp, xConnection, null); + } catch (com.sun.star.bridge.BridgeExistsException e) { + throw new com.sun.star.uno.RuntimeException(e); + } + } + rootObject = xBridge.getInstance(rootOid); + return rootObject; + } + } + + + /** + * Gives a factory for creating the service. + * + * <p>This method is called by the <code>JavaLoader</code>.</p> + * + * @param implName the name of the implementation for which a service is desired. + * @param multiFactory the service manager to be uses if needed. + * @param regKey the registryKey. + * @return returns a <code>XSingleServiceFactory</code> for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals(UrlResolver.class.getName()) ) + xSingleServiceFactory = FactoryHelper.getServiceFactory(_UrlResolver.class, + _UrlResolver.__serviceName, + multiFactory, + regKey); + + return xSingleServiceFactory; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/connections/pipe/PipeConnection.java b/ridljar/com/sun/star/lib/connections/pipe/PipeConnection.java new file mode 100644 index 000000000..b3a686f17 --- /dev/null +++ b/ridljar/com/sun/star/lib/connections/pipe/PipeConnection.java @@ -0,0 +1,213 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.connections.pipe; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.StringTokenizer; + +import com.sun.star.connection.XConnection; +import com.sun.star.connection.XConnectionBroadcaster; +import com.sun.star.io.XStreamListener; +import com.sun.star.lib.util.NativeLibraryLoader; + +/** + * The PipeConnection implements the <code>XConnection</code> interface + * and is uses by the <code>PipeConnector</code> and the <code>PipeAcceptor</code>. + * This class is not part of the provided <code>api</code>. + * + * The native implementation is in jurt/source/pipe/com_sun_star_lib_connections_pipe_PipeConnection.c + * + * @see com.sun.star.lib.connections.pipe.pipeAcceptor + * @see com.sun.star.lib.connections.pipe.pipeConnector + * @see com.sun.star.connection.XConnection + * @since UDK1.0 + */ +public class PipeConnection implements XConnection, XConnectionBroadcaster { + /** + * When set to true, enables various debugging output. + */ + public static final boolean DEBUG = false; + + static { + // load shared library for JNI code + NativeLibraryLoader.loadLibrary(PipeConnection.class.getClassLoader(), "jpipe"); + } + + protected String _aDescription; + protected long _nPipeHandle; + protected ArrayList<XStreamListener> _aListeners; + protected boolean _bFirstRead; + + /** + * Constructs a new <code>PipeConnection</code>. + * + * @param description the description of the connection. + */ + public PipeConnection(String description) + throws IOException + { + if (DEBUG) System.err.println("##### " + getClass().getName() + " - instantiated " + description ); + + _aListeners = new ArrayList<XStreamListener>(); + _bFirstRead = true; + + // get pipe name from pipe descriptor + String aPipeName ; + StringTokenizer aTokenizer = new StringTokenizer( description, "," ); + if ( aTokenizer.hasMoreTokens() ) { + String aConnType = aTokenizer.nextToken(); + if ( !aConnType.equals( "pipe" ) ) + throw new RuntimeException( "invalid pipe descriptor: does not start with 'pipe,'" ); + + String aPipeNameParam = aTokenizer.nextToken(); + if ( !aPipeNameParam.substring( 0, 5 ).equals( "name=" ) ) + throw new RuntimeException( "invalid pipe descriptor: no 'name=' parameter found" ); + aPipeName = aPipeNameParam.substring( 5 ); + } + else + throw new RuntimeException( "invalid or empty pipe descriptor" ); + + // create the pipe + try { + createJNI( aPipeName ); + } catch ( java.lang.Exception ex1 ) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + public void addStreamListener(XStreamListener aListener ) throws com.sun.star.uno.RuntimeException { + _aListeners.add(aListener); + } + + public void removeStreamListener(XStreamListener aListener ) throws com.sun.star.uno.RuntimeException { + _aListeners.remove(aListener); + } + + private void notifyListeners_open() { + for (XStreamListener xStreamListener : _aListeners) { + xStreamListener.started(); + } + } + + private void notifyListeners_close() { + for (XStreamListener xStreamListener : _aListeners) { + xStreamListener.closed(); + } + } + + private void notifyListeners_error(com.sun.star.uno.Exception exception) { + for (XStreamListener xStreamListener : _aListeners) { + xStreamListener.error(exception); + } + } + + // JNI implementation to create the pipe + private native int createJNI( String name ) + throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException; + + // JNI implementation to read from the pipe + private native int readJNI(/*OUT*/byte[][] bytes, int nBytesToRead) + throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException; + + // JNI implementation to write to the pipe + private native void writeJNI(byte aData[]) + throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException; + + // JNI implementation to flush the pipe + private native void flushJNI() + throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException; + + // JNI implementation to close the pipe + private native void closeJNI() + throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException; + + /** + * Read the required number of bytes. + * + * @param bytes the outparameter, where the bytes have to be placed. + * @param nBytesToRead the number of bytes to read. + * @return the number of bytes read. + * + * @see com.sun.star.connection.XConnection#read + */ + public int read(/*OUT*/byte[][] bytes, int nBytesToRead) + throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException + { + if(_bFirstRead) { + _bFirstRead = false; + + notifyListeners_open(); + } + + return readJNI( bytes, nBytesToRead ); + } + + /** + * Write bytes. + * + * @param aData the bytes to write. + * @see com.sun.star.connection.XConnection#write + */ + public void write(byte aData[]) + throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException + { + writeJNI( aData ); + } + + /** + * Flushes the buffer. + * + * @see com.sun.star.connection.XConnection#flush + */ + public void flush() + throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException + { + flushJNI(); + } + + /** + * Closes the connection. + * + * @see com.sun.star.connection.XConnection#close + */ + public void close() + throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException + { + if (DEBUG) System.out.print( "PipeConnection::close() " ); + closeJNI(); + notifyListeners_close(); + if (DEBUG) System.out.println( "done" ); + } + + /** + * Gives a description of the connection. + * + * @return the description. + * @see com.sun.star.connection.XConnection#getDescription + */ + public String getDescription() throws com.sun.star.uno.RuntimeException { + return _aDescription; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/connections/pipe/pipeAcceptor.java b/ridljar/com/sun/star/lib/connections/pipe/pipeAcceptor.java new file mode 100644 index 000000000..983eb3265 --- /dev/null +++ b/ridljar/com/sun/star/lib/connections/pipe/pipeAcceptor.java @@ -0,0 +1,123 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.connections.pipe; + +import com.sun.star.comp.loader.FactoryHelper; +import com.sun.star.connection.AlreadyAcceptingException; +import com.sun.star.connection.ConnectionSetupException; +import com.sun.star.connection.XAcceptor; +import com.sun.star.connection.XConnection; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +/** + * A component that implements the <code>XAcceptor</code> interface. + * + * <p>The <code>pipeAcceptor</code> is a specialized component that uses TCP + * pipes for communication. The <code>pipeAcceptor</code> is generally used + * by the <code>com.sun.star.connection.Acceptor</code> service.</p> + * + * @see com.sun.star.connection.XAcceptor + * @see com.sun.star.connection.XConnection + * @see com.sun.star.connection.XConnector + * @see com.sun.star.comp.loader.JavaLoader + * + * @since UDK 1.0 + */ +public final class pipeAcceptor implements XAcceptor { + /** + * The name of the service. + * + * <p>The <code>JavaLoader</code> accesses this through reflection.</p> + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.pipeAcceptor"; + + /** + * Returns a factory for creating the service. + * + * <p>This method is called by the <code>JavaLoader</code>.</p> + * + * @param implName the name of the implementation for which a service is + * requested. + * @param multiFactory the service manager to be used (if needed). + * @param regKey the registry key. + * @return an <code>XSingleServiceFactory</code> for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) + { + return implName.equals(pipeAcceptor.class.getName()) + ? FactoryHelper.getServiceFactory( + pipeAcceptor.class, __serviceName, multiFactory, regKey) + : null; + } + + /** + * Accepts a connection request via the described pipe. + * + * <p>This call blocks until a connection has been established.</p> + * + * <p>The connection description has the following format: + * <code><var>type</var></code><!-- + * -->*(<code><var>key</var>=<var>value</var></code>), + * where <code><var>type</var></code> should be <code>pipe</code> + * (ignoring case). Supported keys (ignoring case) currently are</p> + * <dl> + * <dt><code>host</code> + * <dd>The name or address of the accepting interface (defaults to + * <code>0</code>, meaning any interface). + * <dt><code>port</code> + * <dd>The TCP port number to accept on (defaults to <code>6001</code>). + * <dt><code>backlog</code> + * <dd>The maximum length of the acceptor's queue (defaults to + * <code>50</code>). + * <dt><code>tcpnodelay</code> + * <dd>A flag (<code>0</code>/<code>1</code>) enabling or disabling Nagle's + * algorithm on the resulting connection. + * </dl> + * + * @param connectionDescription the description of the connection. + * @return an <code>XConnection</code> to the client. + * + * @see com.sun.star.connection.XConnection + * @see com.sun.star.connection.XConnector + */ + public XConnection accept(String connectionDescription) throws + AlreadyAcceptingException, ConnectionSetupException, + com.sun.star.lang.IllegalArgumentException + { + throw new java.lang.NoSuchMethodError( "pipeAcceptor not fully implemented yet" ); + } + + /** + * + * @see com.sun.star.connection.XAcceptor#stopAccepting + */ + public void stopAccepting() { + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/connections/pipe/pipeConnector.java b/ridljar/com/sun/star/lib/connections/pipe/pipeConnector.java new file mode 100644 index 000000000..192d35007 --- /dev/null +++ b/ridljar/com/sun/star/lib/connections/pipe/pipeConnector.java @@ -0,0 +1,120 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.connections.pipe; + +import com.sun.star.comp.loader.FactoryHelper; +import com.sun.star.connection.ConnectionSetupException; +import com.sun.star.connection.NoConnectException; +import com.sun.star.connection.XConnection; +import com.sun.star.connection.XConnector; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +/** + * A component that implements the <code>XConnector</code> interface. + * + * <p>The <code>pipeConnector</code> is a specialized component that uses TCP + * pipes for communication. The <code>pipeConnector</code> is generally + * used by the <code>com.sun.star.connection.Connector</code> service.</p> + * + * @see com.sun.star.connection.XAcceptor + * @see com.sun.star.connection.XConnection + * @see com.sun.star.connection.XConnector + * @see com.sun.star.comp.loader.JavaLoader + * + * @since UDK 1.0 + */ +public final class pipeConnector implements XConnector { + /** + * The name of the service. + * + * <p>The <code>JavaLoader</code> accesses this through reflection.</p> + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName = "com.sun.star.connection.pipeConnector"; + + /** + * Returns a factory for creating the service. + * + * <p>This method is called by the <code>JavaLoader</code>.</p> + * + * @param implName the name of the implementation for which a service is + * requested. + * @param multiFactory the service manager to be used (if needed). + * @param regKey the registry key. + * @return an <code>XSingleServiceFactory</code> for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) + { + return implName.equals(pipeConnector.class.getName()) + ? FactoryHelper.getServiceFactory(pipeConnector.class, + __serviceName, multiFactory, + regKey) + : null; + } + + /** + * Connects via the described pipe to a waiting server. + * + * <p>The connection description has the following format: + * <code><var>type</var></code><!-- + * -->*(<code><var>key</var>=<var>value</var></code>), + * where <code><var>type</var></code> should be <code>pipe</code> + * (ignoring case). Supported keys (ignoring case) currently are</p> + * <dl> + * <dt><code>host</code> + * <dd>The name or address of the server. Must be present. + * <dt><code>port</code> + * <dd>The TCP port number of the server (defaults to <code>6001</code>). + * <dt><code>tcpnodelay</code> + * <dd>A flag (<code>0</code>/<code>1</code>) enabling or disabling Nagle's + * algorithm on the resulting connection. + * </dl> + * + * @param connectionDescription the description of the connection. + * @return an <code>XConnection</code> to the server. + * + * @see com.sun.star.connection.XAcceptor + * @see com.sun.star.connection.XConnection + */ + public synchronized XConnection connect(String connectionDescription) + throws NoConnectException, ConnectionSetupException + { + if (bConnected) + throw new ConnectionSetupException("already connected"); + + try { + XConnection xConn = new PipeConnection( connectionDescription ); + bConnected = true; + return xConn; + } catch ( java.io.IOException e ) { + throw new NoConnectException(e); + } + } + + private boolean bConnected = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/connections/socket/ConnectionDescriptor.java b/ridljar/com/sun/star/lib/connections/socket/ConnectionDescriptor.java new file mode 100644 index 000000000..5a88d41ba --- /dev/null +++ b/ridljar/com/sun/star/lib/connections/socket/ConnectionDescriptor.java @@ -0,0 +1,98 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.connections.socket; + +/** + * Helper class for <code>socketAcceptor</code> and <code>socketConnector</code>. + * + * <p>FIXME: Once those classes have been moved from <code>jurt</code> to + * <code>javaunohelper</code>, they should use + * <code>com.sun.star.lib.uno.helper.UnoUrl</code> either instead of this class + * or underneath this class.</p> + */ +final class ConnectionDescriptor { + public ConnectionDescriptor(String description) + throws com.sun.star.lang.IllegalArgumentException { + for (int i = description.indexOf(','); i >= 0;) { + int j = description.indexOf(',', i + 1); + int k = j < 0 ? description.length() : j; + int l = description.indexOf('=', i + 1); + if (l < 0 || l >= k) { + throw new com.sun.star.lang.IllegalArgumentException( + "parameter lacks '='"); + } + String key = description.substring(i + 1, l); + String value = description.substring(l + 1, k); + if (key.equalsIgnoreCase("host")) { + host = value; + } else if (key.equalsIgnoreCase("port")) { + try { + port = Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new com.sun.star.lang.IllegalArgumentException(e); + } + if (port < 0 || port > 65535) { + throw new com.sun.star.lang.IllegalArgumentException( + "port parameter must have value between 0 and 65535," + + " inclusive"); + } + } else if (key.equalsIgnoreCase("backlog")) { + try { + backlog = Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new com.sun.star.lang.IllegalArgumentException(e); + } + } else if (key.equalsIgnoreCase("tcpnodelay")) { + if (value.equals("0")) { + tcpNoDelay = Boolean.FALSE; + } else if (value.equals("1")) { + tcpNoDelay = Boolean.TRUE; + } else { + throw new com.sun.star.lang.IllegalArgumentException( + "tcpnodelay parameter must have 0/1 value"); + } + } + i = j; + } + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public int getBacklog() { + return backlog; + } + + public Boolean getTcpNoDelay() { + return tcpNoDelay; + } + + private String host = null; + private int port = 6001; + private int backlog = 50; + private Boolean tcpNoDelay = null; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/connections/socket/SocketConnection.java b/ridljar/com/sun/star/lib/connections/socket/SocketConnection.java new file mode 100644 index 000000000..a906496f2 --- /dev/null +++ b/ridljar/com/sun/star/lib/connections/socket/SocketConnection.java @@ -0,0 +1,235 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.connections.socket; + + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.Socket; + +import java.util.ArrayList; +import java.util.Arrays; + +import com.sun.star.connection.XConnection; +import com.sun.star.connection.XConnectionBroadcaster; +import com.sun.star.io.XStreamListener; + +/** + * The SocketConnection implements the <code>XConnection</code> interface + * and is uses by the <code>SocketConnector</code> and the <code>SocketAcceptor</code>. + * + * <p>This class is not part of the provided <code>api</code>.</p> + * + * @see com.sun.star.lib.connections.socket.socketAcceptor + * @see com.sun.star.lib.connections.socket.socketConnector + * @see com.sun.star.connection.XConnection + * @since UDK1.0 + */ +public class SocketConnection implements XConnection, XConnectionBroadcaster { + /** + * When set to true, enables various debugging output. + */ + public static final boolean DEBUG = false; + + protected String _description; + protected Socket _socket; + protected InputStream _inputStream; + protected OutputStream _outputStream; + protected ArrayList<XStreamListener> _listeners; + protected boolean _firstRead; + + /** + * Constructs a new <code>SocketConnection</code>. + * + * @param description the description of the connection. + * @param socket the socket of the connection. + */ + public SocketConnection(String description, Socket socket) throws IOException { + if (DEBUG) System.err.println("##### " + getClass().getName() + " - instantiated " + description + " " + socket); + + _description = description + + ",localHost=" + socket.getLocalAddress().getHostName() + + ",localPort=" + socket.getLocalPort() + + ",peerHost=" + socket.getInetAddress().getHostName() + + ",peerPort=" + socket.getPort(); + + _socket = socket; + _inputStream = new BufferedInputStream(socket.getInputStream()); + _outputStream = new BufferedOutputStream(socket.getOutputStream()); + + _listeners = new ArrayList<XStreamListener>(); + _firstRead = true; + } + + public void addStreamListener(XStreamListener aListener ) + throws com.sun.star.uno.RuntimeException { + _listeners.add(aListener); + } + + public void removeStreamListener(XStreamListener aListener ) + throws com.sun.star.uno.RuntimeException { + _listeners.remove(aListener); + } + + private void notifyListeners_open() { + for (XStreamListener xStreamListener : _listeners) { + xStreamListener.started(); + } + } + + private void notifyListeners_close() { + for (XStreamListener xStreamListener : _listeners) { + xStreamListener.closed(); + } + } + + private void notifyListeners_error(com.sun.star.uno.Exception exception) { + for (XStreamListener xStreamListener : _listeners) { + xStreamListener.error(exception); + } + } + + /** + * Read the required number of bytes. + * + * @param bytes the outparameter, where the bytes have to be placed. + * @param nBytesToRead the number of bytes to read. + * @return the number of bytes read. + * + * @see com.sun.star.connection.XConnection#read + */ + public int read(/*OUT*/byte[][] bytes, int nBytesToRead) + throws com.sun.star.io.IOException, com.sun.star.uno.RuntimeException { + if(_firstRead) { + _firstRead = false; + + notifyListeners_open(); + } + + String errMessage = null; + + int read_bytes = 0; + bytes[0] = new byte[nBytesToRead]; + + try { + int count ; + + do { + count = _inputStream.read(bytes[0], read_bytes, nBytesToRead - read_bytes); + if(count == -1) + errMessage = "EOF reached - " + getDescription(); + + read_bytes += count; + } + while(read_bytes >= 0 && read_bytes < nBytesToRead && count >= 0); + } catch(IOException ioException) { + if(DEBUG) { + System.err.println("##### " + getClass().getName() + ".read - exception occurred:" + ioException); + ioException.printStackTrace(); + } + + errMessage = ioException.toString(); + } + + if(errMessage != null) { + com.sun.star.io.IOException unoIOException = new com.sun.star.io.IOException(errMessage); + notifyListeners_error(unoIOException); + + throw unoIOException; + } + + if (DEBUG) System.err.println("##### " + getClass().getName() + " - read byte:" + read_bytes + " " + Arrays.toString(bytes[0])); + + return read_bytes; + } + + /** + * Write bytes. + * + * @param aData the bytes to write. + * @see com.sun.star.connection.XConnection#write + */ + public void write(byte aData[]) throws com.sun.star.io.IOException, + com.sun.star.uno.RuntimeException { + try { + _outputStream.write(aData); + } catch(IOException ioException) { + com.sun.star.io.IOException unoIOException = new com.sun.star.io.IOException(ioException); + notifyListeners_error(unoIOException); + + throw unoIOException; + } + + if (DEBUG) System.err.println("##### " + getClass().getName() + " - written bytes:" + Arrays.toString(aData) + " " + aData.length); + } + + /** + * Flushes the buffer. + * + * @see com.sun.star.connection.XConnection#flush + */ + public void flush() throws com.sun.star.io.IOException, + com.sun.star.uno.RuntimeException { + try { + _outputStream.flush(); + } catch(IOException ioException) { + com.sun.star.io.IOException unoIOException = new com.sun.star.io.IOException(ioException); + notifyListeners_error(unoIOException); + + throw unoIOException; + } + } + + /** + * Closes the connection. + * + * @see com.sun.star.connection.XConnection#close + */ + public void close() throws com.sun.star.io.IOException, + com.sun.star.uno.RuntimeException { + try { + _socket.close(); + } catch(IOException ioException) { + com.sun.star.io.IOException unoIOException = new com.sun.star.io.IOException(ioException); + notifyListeners_error(unoIOException); + + throw unoIOException; + } + if (DEBUG) System.err.println("##### " + getClass().getName() + " - socket closed"); + + notifyListeners_close(); + } + + /** + * Gives a description of the connection. + * + * @return the description. + * @see com.sun.star.connection.XConnection#getDescription + */ + public String getDescription() throws com.sun.star.uno.RuntimeException { + return _description; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/connections/socket/socketAcceptor.java b/ridljar/com/sun/star/lib/connections/socket/socketAcceptor.java new file mode 100644 index 000000000..4000a1d0a --- /dev/null +++ b/ridljar/com/sun/star/lib/connections/socket/socketAcceptor.java @@ -0,0 +1,202 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.connections.socket; + +import com.sun.star.comp.loader.FactoryHelper; +import com.sun.star.connection.AlreadyAcceptingException; +import com.sun.star.connection.ConnectionSetupException; +import com.sun.star.connection.XAcceptor; +import com.sun.star.connection.XConnection; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +import java.io.IOException; +import java.net.*; + +/** + * A component that implements the <code>XAcceptor</code> interface. + * + * <p>The <code>socketAcceptor</code> is a specialized component that uses TCP + * sockets for communication. The <code>socketAcceptor</code> is generally used + * by the <code>com.sun.star.connection.Acceptor</code> service.</p> + * + * @see com.sun.star.connection.XAcceptor + * @see com.sun.star.connection.XConnection2 + * @see com.sun.star.connection.XConnector + * @see com.sun.star.comp.loader.JavaLoader + * + * @since UDK 1.0 + */ +public final class socketAcceptor implements XAcceptor { + /** + * The name of the service. + * + * <p>The <code>JavaLoader</code> accesses this through reflection.</p> + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.socketAcceptor"; + + /** + * Returns a factory for creating the service. + * + * <p>This method is called by the <code>JavaLoader</code>.</p> + * + * @param implName the name of the implementation for which a service is + * requested. + * @param multiFactory the service manager to be used (if needed). + * @param regKey the registry key. + * @return an <code>XSingleServiceFactory</code> for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) + { + return implName.equals(socketAcceptor.class.getName()) + ? FactoryHelper.getServiceFactory(socketAcceptor.class, + __serviceName, multiFactory, + regKey) + : null; + } + + /** + * Accepts a connection request via the described socket. + * + * <p>This call blocks until a connection has been established.</p> + * + * <p>The connection description has the following format: + * <code><var>type</var></code><!-- + * -->*(<code><var>key</var>=<var>value</var></code>), + * where <code><var>type</var></code> should be <code>socket</code> + * (ignoring case). Supported keys (ignoring case) currently are</p> + * <dl> + * <dt><code>host</code> + * <dd>The name or address of the accepting interface (defaults to + * <code>0</code>, meaning any interface). + * <dt><code>port</code> + * <dd>The TCP port number to accept on (defaults to <code>6001</code>). + * <dt><code>backlog</code> + * <dd>The maximum length of the acceptor's queue (defaults to + * <code>50</code>). + * <dt><code>tcpnodelay</code> + * <dd>A flag (<code>0</code>/<code>1</code>) enabling or disabling Nagle's + * algorithm on the resulting connection. + * </dl> + * + * @param connectionDescription the description of the connection. + * @return an <code>XConnection</code> to the client. + * + * @see com.sun.star.connection.XConnection + * @see com.sun.star.connection.XConnector + */ + public XConnection accept(String connectionDescription) throws + AlreadyAcceptingException, ConnectionSetupException, + com.sun.star.lang.IllegalArgumentException + { + ServerSocket serv; + synchronized (this) { + if (server == null) { + ConnectionDescriptor desc + = new ConnectionDescriptor(connectionDescription); + String host = desc.getHost(); + if (host.equals("0")) { + host = null; + } + if (DEBUG) { + System.err.println("##### " + getClass().getName() + + ".accept: creating ServerSocket " + + desc.getPort() + ", " + + desc.getBacklog() + ", " + host); + } + try { + server = new ServerSocket(desc.getPort(), desc.getBacklog(), + host == null ? null + : InetAddress.getByName(host)); + } catch (IOException e) { + throw new ConnectionSetupException(e); + } + acceptingDescription = connectionDescription; + tcpNoDelay = desc.getTcpNoDelay(); + } else if (!connectionDescription.equals(acceptingDescription)) { + throw new AlreadyAcceptingException(acceptingDescription + + " vs. " + + connectionDescription); + } + serv = server; + } + Socket socket = null; + try { + socket = serv.accept(); + if (DEBUG) { + System.err.println("##### " + getClass().getName() + + ".accept: accepted " + socket); + } + // we enable tcpNoDelay for loopback connections because + // it can make a significant speed difference on linux boxes. + if (tcpNoDelay != null) { + socket.setTcpNoDelay(tcpNoDelay.booleanValue()); + } + else { + InetSocketAddress address = (InetSocketAddress)socket.getRemoteSocketAddress(); + if (address != null && address.getAddress().isLoopbackAddress()) { + socket.setTcpNoDelay(true); + } + } + return new SocketConnection(acceptingDescription, socket); + } + catch(IOException e) { + if (socket != null) { + try { + socket.close(); + } catch(IOException ioException) { + } + } + throw new ConnectionSetupException(e); + } + } + + /** + * + * @see com.sun.star.connection.XAcceptor#stopAccepting + */ + public void stopAccepting() { + ServerSocket serv; + synchronized (this) { + serv = server; + } + try { + serv.close(); + } + catch (IOException e) { + throw new com.sun.star.uno.RuntimeException(e); + } + } + + private static final boolean DEBUG = false; + + private ServerSocket server = null; + private String acceptingDescription; + private Boolean tcpNoDelay; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/connections/socket/socketConnector.java b/ridljar/com/sun/star/lib/connections/socket/socketConnector.java new file mode 100644 index 000000000..484ffd135 --- /dev/null +++ b/ridljar/com/sun/star/lib/connections/socket/socketConnector.java @@ -0,0 +1,176 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.connections.socket; + +import com.sun.star.comp.loader.FactoryHelper; +import com.sun.star.connection.ConnectionSetupException; +import com.sun.star.connection.NoConnectException; +import com.sun.star.connection.XConnection; +import com.sun.star.connection.XConnector; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +/** + * A component that implements the <code>XConnector</code> interface. + * + * <p>The <code>socketConnector</code> is a specialized component that uses TCP + * sockets for communication. The <code>socketConnector</code> is generally + * used by the <code>com.sun.star.connection.Connector</code> service.</p> + * + * @see com.sun.star.connection.XAcceptor + * @see com.sun.star.connection.XConnection + * @see com.sun.star.connection.XConnector + * @see com.sun.star.comp.loader.JavaLoader + * + * @since UDK 1.0 + */ +public final class socketConnector implements XConnector { + /** + * The name of the service. + * + * <p>The <code>JavaLoader</code> accesses this through reflection.</p> + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static final String __serviceName + = "com.sun.star.connection.socketConnector"; + + /** + * Returns a factory for creating the service. + * + * <p>This method is called by the <code>JavaLoader</code>.</p> + * + * @param implName the name of the implementation for which a service is + * requested. + * @param multiFactory the service manager to be used (if needed). + * @param regKey the registry key. + * @return an <code>XSingleServiceFactory</code> for creating the component. + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) + { + return implName.equals(socketConnector.class.getName()) + ? FactoryHelper.getServiceFactory(socketConnector.class, + __serviceName, multiFactory, + regKey) + : null; + } + + /** + * Connects via the described socket to a waiting server. + * + * <p>The connection description has the following format: + * <code><var>type</var></code><!-- + * -->*(<code><var>key</var>=<var>value</var></code>), + * where <code><var>type</var></code> should be <code>socket</code> + * (ignoring case). Supported keys (ignoring case) currently are</p> + * <dl> + * <dt><code>host</code> + * <dd>The name or address of the server. Must be present. + * <dt><code>port</code> + * <dd>The TCP port number of the server (defaults to <code>6001</code>). + * <dt><code>tcpnodelay</code> + * <dd>A flag (<code>0</code>/<code>1</code>) enabling or disabling Nagle's + * algorithm on the resulting connection. + * </dl> + * + * @param connectionDescription the description of the connection. + * @return an <code>XConnection</code> to the server. + * + * @see com.sun.star.connection.XAcceptor + * @see com.sun.star.connection.XConnection + */ + public synchronized XConnection connect(String connectionDescription) + throws NoConnectException, ConnectionSetupException + { + if (connected) + throw new ConnectionSetupException("already connected"); + + ConnectionDescriptor desc; + try { + desc = new ConnectionDescriptor(connectionDescription); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new ConnectionSetupException(e); + } + + if (desc.getHost() == null) + throw new ConnectionSetupException("host parameter missing"); + // Try all (IPv4 and IPv6) addresses, in case this client is on a + // dual-stack host and the server process is an IPv4-only process, also + // on a dual-stack host (see Stevens, Fenner, Rudoff: "Unix Network + // Programming, Volume 1: The Sockets Networking API, 3rd Edition", + // p. 359): + InetAddress[] adr; + try { + adr = InetAddress.getAllByName(desc.getHost()); + } catch (UnknownHostException e) { + throw new ConnectionSetupException(e); + } + Socket socket = null; + boolean isLoopbackAddress = false; + for (int i = 0; i < adr.length; ++i) { + try { + isLoopbackAddress = adr[i].isLoopbackAddress(); + socket = new Socket(adr[i], desc.getPort()); + break; + } catch (IOException e) { + if (i == adr.length - 1) + throw new NoConnectException(e); + } + } + + if (socket == null) + throw new ConnectionSetupException("no socket"); + + XConnection con; + try { + // we enable tcpNoDelay for loopback connections because + // it can make a significant speed difference on linux boxes. + if (desc.getTcpNoDelay() != null) + socket.setTcpNoDelay(desc.getTcpNoDelay().booleanValue()); + else if (isLoopbackAddress) + socket.setTcpNoDelay(true); + + con = new SocketConnection(connectionDescription, socket); + } catch (IOException e) { + if (socket != null) { + try { + socket.close(); + } catch(IOException ioException) { + } + } + throw new NoConnectException(e); + } + connected = true; + return con; + } + + private boolean connected = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/Proxy.java b/ridljar/com/sun/star/lib/uno/Proxy.java new file mode 100644 index 000000000..7d3612758 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/Proxy.java @@ -0,0 +1,34 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno; + +/** + * Marker interface implemented by proxies for UNO objects. + * + * <p>Currently, this interface is used internally by + * <code>com.sun.star.lib.uno.environments.java.java_environment</code> to + * distinguish between proxies and local objects. Any proxy object that shall + * be registered at the <code>java_environment</code> must implement this marker + * interface.</p> + */ +public interface Proxy { +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/adapter/ByteArrayToXInputStreamAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/ByteArrayToXInputStreamAdapter.java new file mode 100644 index 000000000..2d3e9a848 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/adapter/ByteArrayToXInputStreamAdapter.java @@ -0,0 +1,117 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.adapter; + +import com.sun.star.io.XInputStream; +import com.sun.star.io.XSeekable; +import com.sun.star.lib.uno.helper.ComponentBase; + +public final class ByteArrayToXInputStreamAdapter + extends ComponentBase + implements XInputStream, XSeekable +{ + + byte[] m_bytes; + int m_length; + int m_pos; + + boolean m_open; + + /** Creates a new instance of ByteArrayXInputStram */ + public ByteArrayToXInputStreamAdapter(byte[] bytes) { + init(bytes); + } + + public void init(byte[] bytes) { + m_bytes = bytes; + m_length = bytes.length; + m_pos = 0; + m_open = true; + } + + private void _check() throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException { + if (m_bytes == null) { + throw new com.sun.star.io.NotConnectedException("no bytes"); + } + if(!m_open) { + throw new com.sun.star.io.IOException("input closed"); + } + } + + public int available() throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException { + _check(); + long a = m_length - m_pos; + if (a != (int)a) + throw new com.sun.star.io.IOException("integer overflow"); + else { + return (int)a; + } + } + + public void closeInput() throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException { + _check(); + m_open = false; + } + + public int readBytes(byte[][] values, int param) throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + _check(); + try { + int remain = (m_length - m_pos); + if (param > remain) param = remain; + /* ARGH!!! */ + if (values[0] == null){ + values[0] = new byte[param]; + } + System.arraycopy(m_bytes, m_pos, values[0], 0, param); + m_pos += param; + return param; + } catch (ArrayIndexOutOfBoundsException ex) { + throw new com.sun.star.io.BufferSizeExceededException(ex, "buffer overflow"); + } catch (Exception ex) { + throw new com.sun.star.io.IOException(ex, "error accessing buffer"); + } + } + + public int readSomeBytes(byte[][] values, int param) throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + return readBytes(values, param); + } + + public void skipBytes(int param) throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + _check(); + if (param > (m_length - m_pos)) + throw new com.sun.star.io.BufferSizeExceededException("buffer overflow"); + m_pos += param; + } + + public long getLength() throws com.sun.star.io.IOException { + if (m_bytes != null) return m_length; + else throw new com.sun.star.io.IOException("no bytes"); + } + + public long getPosition() throws com.sun.star.io.IOException { + if (m_bytes != null) return m_pos; + else throw new com.sun.star.io.IOException("no bytes"); + } + + public void seek(long param) throws com.sun.star.lang.IllegalArgumentException, com.sun.star.io.IOException { + if (m_bytes != null){ + if (param < 0 || param > m_length) throw new com.sun.star.lang.IllegalArgumentException("invalid seek position"); + else m_pos = (int)param; + }else throw new com.sun.star.io.IOException("no bytes"); + } +} diff --git a/ridljar/com/sun/star/lib/uno/adapter/InputStreamToXInputStreamAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/InputStreamToXInputStreamAdapter.java new file mode 100644 index 000000000..3d60e5a11 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/adapter/InputStreamToXInputStreamAdapter.java @@ -0,0 +1,145 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.adapter; + +import java.io.IOException; + +import com.sun.star.io.XInputStream; + +import java.io.InputStream; + +/** The <code>InputStreamToInputXStreamAdapter</code> wraps the + Java <code>InputStream</code> object into a + UNO <code>XInputStream</code> object. + This allows users to access an <code>InputStream</code> + as if it were an <code>XInputStream</code>. + */ +public final class InputStreamToXInputStreamAdapter implements XInputStream { + + /** + * Internal store to the InputStream + */ + private final InputStream iIn; + + /** + * Constructor. + * + * @param in The <code>XInputStream</code> to be + * accessed as an <code>InputStream</code>. + */ + public InputStreamToXInputStreamAdapter (InputStream in) + { + iIn = in; + } + + public int available() throws + com.sun.star.io.IOException + { + + int bytesAvail; + + try { + bytesAvail = iIn.available(); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + + return bytesAvail; + } + + public void closeInput() throws + com.sun.star.io.IOException + { + try { + iIn.close(); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + } + + public int readBytes(byte[][] b, int len) throws + com.sun.star.io.IOException + { + try { + long bytesRead; + if (len >iIn.available()) { + bytesRead = iIn.read(b[0], 0, iIn.available()); + } + else{ + bytesRead = iIn.read(b[0], 0, len); + } + // Casting bytesRead to an int is okay, since the user can + // only pass in an integer length to read, so the bytesRead + // must <= len. + + if (bytesRead <= 0) { + return 0; + } + return ((int)bytesRead); + + + } catch (IOException e) { + throw new com.sun.star.io.IOException("reader error", e); + } + } + + public int readSomeBytes(byte[][] b, int len) throws + com.sun.star.io.IOException + { + try { + long bytesRead; + if (len >iIn.available()) { + bytesRead = iIn.read(b[0], 0, iIn.available()); + } + else{ + bytesRead = iIn.read(b[0], 0, len); + } + // Casting bytesRead to an int is okay, since the user can + // only pass in an integer length to read, so the bytesRead + // must <= len. + + if (bytesRead <= 0) { + return 0; + } + return ((int)bytesRead); + + + } catch (IOException e) { + throw new com.sun.star.io.IOException("reader error", e); + } + } + + public void skipBytes(int n) throws + com.sun.star.io.IOException + { + try { + iIn.available(); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + + do { + try { + n -= iIn.skip(n); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + } while (n > 0); + } +} + diff --git a/ridljar/com/sun/star/lib/uno/adapter/OutputStreamToXOutputStreamAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/OutputStreamToXOutputStreamAdapter.java new file mode 100644 index 000000000..2842e3df0 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/adapter/OutputStreamToXOutputStreamAdapter.java @@ -0,0 +1,80 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.adapter; + +import java.io.IOException; + +import com.sun.star.io.XOutputStream; + +import java.io.OutputStream; + +/** The <code>OutputStreamToXOutputStreamAdapter</code> wraps + a UNO <code>XOutputStream</code> into a Java <code>OutputStream</code> + object in a Java. This allows users to access an <code>OutputStream</code> + as if it were an <code>XOutputStream</code>. + */ +public final class OutputStreamToXOutputStreamAdapter implements XOutputStream { + + /** + * Internal handle to the OutputStream + */ + OutputStream iOut; + + /** + * Constructor. + * + * @param out The <code>XOutputStream</code> to be + * accessed as an <code>OutputStream</code>. + */ + public OutputStreamToXOutputStreamAdapter(OutputStream out) { + iOut = out; + } + + public void closeOutput() throws + com.sun.star.io.IOException + { + try { + iOut.close(); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + } + + public void flush() throws + com.sun.star.io.IOException + { + try { + iOut.flush(); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + } + + public void writeBytes(byte[] b) throws + com.sun.star.io.IOException + { + + try { + iOut.write(b); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + } + +} diff --git a/ridljar/com/sun/star/lib/uno/adapter/XInputStreamToInputStreamAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/XInputStreamToInputStreamAdapter.java new file mode 100644 index 000000000..25f1798fb --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/adapter/XInputStreamToInputStreamAdapter.java @@ -0,0 +1,218 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.adapter; + +import java.io.IOException; + +import com.sun.star.io.XInputStream; + +import java.io.InputStream; + +/** + * The <code>XInputStreamToInputStreamAdapter</code> wraps + * the UNO <code>XInputStream</code> object in a Java + * <code>InputStream</code>. This allows users to access + * an <code>XInputStream</code> as if it were an + * <code>InputStream</code>. + */ +public final class XInputStreamToInputStreamAdapter extends InputStream { + + /** + * Internal handle to the XInputStream + */ + private final XInputStream xin; + + /** + * Constructor. + * + * @param in The <code>XInputStream</code> to be + * accessed as an <code>InputStream</code>. + */ + public XInputStreamToInputStreamAdapter (XInputStream in) { + xin = in; + } + + @Override + public int available() throws IOException { + + int bytesAvail; + + try { + bytesAvail = xin.available(); + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + + return bytesAvail; + } + + @Override + public void close() throws IOException { + try { + xin.closeInput(); + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + } + + @Override + public int read () throws IOException { + byte [][] tmp = new byte [1][1]; + try { + long bytesRead = xin.readBytes(tmp, 1); + + if (bytesRead <= 0) { + return (-1); + } else { + int tmpInt = tmp[0][0]; + if (tmpInt< 0 ){ + tmpInt = 256 +tmpInt; + } + return tmpInt; + } + + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + } + + @Override + public int read (byte[] b) throws IOException { + + byte [][] tmp = new byte [1][b.length]; + int bytesRead; + + try { + bytesRead = xin.readBytes(tmp, b.length); + if (bytesRead <= 0) { + return -1; + } else if (bytesRead < b.length) { + System.arraycopy(tmp[0], 0, b, 0, bytesRead); + } else { + System.arraycopy(tmp[0], 0, b, 0, b.length); + } + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + + return bytesRead; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + byte [][] tmp = new byte [1][b.length]; + try { + long bytesRead; + int av = xin.available(); + if ( av != 0 && len > av) { + bytesRead = xin.readBytes(tmp, av); + } + else{ + bytesRead = xin.readBytes(tmp,len); + } + // Casting bytesRead to an int is okay, since the user can + // only pass in an integer length to read, so the bytesRead + // must <= len. + + if (bytesRead <= 0) { + return -1; + } else if (bytesRead < len) { + System.arraycopy(tmp[0], 0, b, off, (int)bytesRead); + } else { + System.arraycopy(tmp[0], 0, b, off, len); + } + + return ((int)bytesRead); + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + } + + @Override + public long skip(long n) throws IOException { + + int avail; + long tmpLongVal = n; + int tmpIntVal; + + try { + avail = xin.available(); + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + + do { + if (tmpLongVal >= Integer.MAX_VALUE) { + tmpIntVal = Integer.MAX_VALUE; + } else { + // Casting is safe here. + tmpIntVal = (int)tmpLongVal; + } + tmpLongVal -= tmpIntVal; + + try { + xin.skipBytes(tmpIntVal); + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + } while (tmpLongVal > 0); + + if ( avail != 0 && avail < n) { + return avail; + } else { + return n; + } + } + + /** + * Tests if this input stream supports the mark and reset methods. + * The markSupported method of + * <code>XInputStreamToInputStreamAdapter</code> returns false. + * + * @return false + */ + @Override + public boolean markSupported() { + return false; + } + + @Override + public synchronized void mark(int readlimit) { + // Not supported. + } + + @Override + public synchronized void reset() throws IOException { + // Not supported. + } +} + diff --git a/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToByteArrayAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToByteArrayAdapter.java new file mode 100644 index 000000000..48871277a --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToByteArrayAdapter.java @@ -0,0 +1,94 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.adapter; + +import com.sun.star.io.*; +import com.sun.star.lib.uno.helper.ComponentBase; + +public final class XOutputStreamToByteArrayAdapter + extends ComponentBase + implements XOutputStream +{ + private static final int initialSize = 100240; // 10 kb + private int size = 0; + private int position = 0; + private boolean externalBuffer = false; + private byte[] buffer; + + /** Creates a new instance of ByteArrayXOutputStream */ + public XOutputStreamToByteArrayAdapter() { + this(null); + } + + public XOutputStreamToByteArrayAdapter(byte[] aBuffer) { + if (aBuffer != null) { + externalBuffer = true; + buffer = aBuffer; + size = buffer.length; + } else { + size = initialSize; + buffer = new byte[size]; + } + } + + public byte[] getBuffer() { + return buffer; + } + + public void closeOutput() + throws com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, + com.sun.star.io.IOException + { + // trim buffer + if ( buffer.length > position && !externalBuffer ) + { + byte[] newBuffer = new byte[position]; + System.arraycopy(buffer, 0, newBuffer, 0, position); + buffer = newBuffer; + } + } + + public void flush() + throws com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, + com.sun.star.io.IOException + { + } + + public void writeBytes(byte[] values) + throws com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, + com.sun.star.io.IOException + { + if ( values.length > size-position ) + { + if ( externalBuffer ) + throw new BufferSizeExceededException("out of buffer space, cannot grow external buffer"); + while ( values.length > size-position ) { + size *= 2; + } + byte[] newBuffer = new byte[size]; + System.arraycopy(buffer, 0, newBuffer, 0, position); + buffer = newBuffer; + } + System.arraycopy(values, 0, buffer, position, values.length); + position += values.length; + } + +} diff --git a/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToOutputStreamAdapter.java b/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToOutputStreamAdapter.java new file mode 100644 index 000000000..8104cf42f --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/adapter/XOutputStreamToOutputStreamAdapter.java @@ -0,0 +1,117 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.adapter; + +import java.io.IOException; + +import com.sun.star.io.XOutputStream; + +import java.io.OutputStream; + +/** + * The <code>XOutputStreamToOutputStreamAdapter</code> wraps + * the UNO <code>XOutputStream</code> object in a Java + * <code>OutputStream</code>. This allows users to access + * an <code>XOutputStream</code> as if it were an + * <code>OutputStream</code>. + */ +public final class XOutputStreamToOutputStreamAdapter extends OutputStream { + + /** + * Internal handle to the XInputStream + */ + XOutputStream xout; + + /** + * Constructor. + * + * @param out The <code>XOutputStream</code> to be + * accessed as an <code>OutputStream</code>. + */ + public XOutputStreamToOutputStreamAdapter(XOutputStream out) { + xout = out; + } + + @Override + public void close() throws IOException { + try { + xout.closeOutput(); + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + } + + @Override + public void flush() throws IOException { + try { + xout.flush(); + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + } + + @Override + public void write(byte[] b) throws IOException { + + try { + xout.writeBytes(b); + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + + byte[] tmp = new byte[len]; + + // Copy the input array into a temp array, and write it out. + + System.arraycopy(b, off, tmp, 0, len); + + try { + xout.writeBytes(tmp); + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + } + + @Override + public void write(int b) throws IOException { + + byte [] oneByte = new byte [1]; + oneByte[0] = (byte) b; + + try { + xout.writeBytes(oneByte); + } catch (Exception e) { + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + } +} diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/BridgedObject.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/BridgedObject.java new file mode 100644 index 000000000..0a724f059 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/BridgedObject.java @@ -0,0 +1,43 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.bridges.java_remote; + +import com.sun.star.bridge.XBridge; + +/** + * A back door to access the bridge associated with a bridged object. + */ +public final class BridgedObject { + /** + * Obtains the bridge associated with a bridged object. + * + * @param obj a reference to a (Java representation of a) UNO object; + * must not be null. + * @return the bridge associated with the given object, if it is indeed + * bridged; otherwise, null is returned. + */ + public static XBridge getBridge(Object obj) { + return ProxyFactory.getBridge(obj); + } + + private BridgedObject() {} // do not instantiate +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java new file mode 100644 index 000000000..1b3848983 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java @@ -0,0 +1,198 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.bridges.java_remote; + +import com.sun.star.bridge.XBridge; +import com.sun.star.lib.util.AsynchronousFinalizer; +import com.sun.star.uno.IQueryInterface; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * A factory for proxies specific to the <code>java_remote_bridge</code>. + * + * <p>Eventually, this class should be united with all other proxy classes + * specific to certain bridges (for example, the JNI bridge), resulting in a + * generic proxy class.</p> + */ +final class ProxyFactory { + public ProxyFactory(RequestHandler requestHandler, XBridge bridge) { + this.requestHandler = requestHandler; + this.bridge = bridge; + } + + public Object create(String oid, Type type) { + return Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { com.sun.star.lib.uno.Proxy.class, + IQueryInterface.class, type.getZClass() }, + new Handler(oid, type)); + } + + public boolean isProxy(Object obj) { + if (Proxy.isProxyClass(obj.getClass())) { + InvocationHandler h = Proxy.getInvocationHandler(obj); + return h instanceof Handler && ((Handler) h).matches(this); + } else { + return false; + } + } + + public void dispose() throws InterruptedException { + asynchronousFinalizer.drain(); + } + + public static XBridge getBridge(Object obj) { + if (Proxy.isProxyClass(obj.getClass())) { + InvocationHandler h = Proxy.getInvocationHandler(obj); + if (h instanceof Handler) { + return ((Handler) h).getBridge(); + } + } + return null; + } + + static int getDebugCount() { + synchronized (debugCountLock) { + return debugCount; + } + } + + private static void incrementDebugCount() { + synchronized (debugCountLock) { + ++debugCount; + } + } + + private static void decrementDebugCount() { + synchronized (debugCountLock) { + --debugCount; + } + } + + private final class Handler implements InvocationHandler { + public Handler(String oid, Type type) { + this.oid = oid; + this.type = type; + incrementDebugCount(); + } + + public boolean matches(ProxyFactory factory) { + return ProxyFactory.this == factory; + } + + public XBridge getBridge() { + return bridge; + } + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable + { + if (method.equals(METHOD_EQUALS) || method.equals(METHOD_IS_SAME)) { + return Boolean.valueOf(args[0] != null + && oid.equals(UnoRuntime.generateOid(args[0]))); + } else if (method.equals(METHOD_HASH_CODE)) { + return Integer.valueOf(oid.hashCode()); + } else if (method.equals(METHOD_TO_STRING)) { + return "[Proxy:" + System.identityHashCode(proxy) + "," + oid + + "," + type + "]"; + } else if (method.equals(METHOD_QUERY_INTERFACE)) { + // See the comment in java_remote_bridge.mapInterfaceTo for one + // reason why this implementation must not satisfy a request for + // a super-interface with a proxy itself: + return args[0].equals(type) ? proxy + : request("queryInterface", args); + } else if (method.equals(METHOD_GET_OID)) { + return oid; + } else { + return request(method.getName(), args); + } + } + + @Override + protected void finalize() { + decrementDebugCount(); + asynchronousFinalizer.add(new AsynchronousFinalizer.Job() { + public void run() throws Throwable { + request("release", null); + } + }); + } + + private Object request(String operation, Object[] args) throws Throwable + { + Object res = requestHandler.sendRequest(oid, type, operation, args); + // Avoid early finalization of this object, while an invoke -> + // request call is still ongoing; as finalize also calls request, + // this should fulfil the condition from The Java Language + // Specification, 3rd ed., that "if an object's finalizer can result + // in synchronization on that object, then that object must be alive + // and considered reachable whenever a lock is held on it:" + synchronized (this) { + ++dummy; + } + return res; + } + + private final String oid; + private final Type type; + @SuppressWarnings("unused") + private int dummy = 0; + } + + private static final Method METHOD_EQUALS; + private static final Method METHOD_HASH_CODE; + private static final Method METHOD_TO_STRING; + private static final Method METHOD_QUERY_INTERFACE; + private static final Method METHOD_IS_SAME; + private static final Method METHOD_GET_OID; + static { + try { + METHOD_EQUALS = Object.class.getMethod( + "equals", new Class[] { Object.class }); + METHOD_HASH_CODE = Object.class.getMethod( + "hashCode", (Class[]) null); + METHOD_TO_STRING = Object.class.getMethod( + "toString", (Class[]) null); + METHOD_QUERY_INTERFACE = IQueryInterface.class.getMethod( + "queryInterface", new Class[] { Type.class }); + METHOD_IS_SAME = IQueryInterface.class.getMethod( + "isSame", new Class[] { Object.class }); + METHOD_GET_OID = IQueryInterface.class.getMethod( + "getOid", (Class[]) null); + } catch (NoSuchMethodException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static final Object debugCountLock = new Object(); + private static int debugCount = 0; + + private final RequestHandler requestHandler; + private final XBridge bridge; + private final AsynchronousFinalizer asynchronousFinalizer = + new AsynchronousFinalizer(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/RequestHandler.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/RequestHandler.java new file mode 100644 index 000000000..d5246bf26 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/RequestHandler.java @@ -0,0 +1,35 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.bridges.java_remote; + +import com.sun.star.uno.Type; + +/** + * The link between the proxies generated by <code>ProxyFactory</code> (which + * receive requests in the form of method calls) and + * <code>java_remote_bridge</code> (which passes those requests on to the remote + * side). + */ +interface RequestHandler { + Object sendRequest(String oid, Type type, String operation, Object[] args) + throws Throwable; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionInputStream_Adapter.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionInputStream_Adapter.java new file mode 100644 index 000000000..8d660e88c --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionInputStream_Adapter.java @@ -0,0 +1,76 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.bridges.java_remote; + + +import java.io.IOException; +import java.io.InputStream; + +import com.sun.star.connection.XConnection; + + +class XConnectionInputStream_Adapter extends InputStream { + private static final boolean DEBUG = false; + + protected XConnection _xConnection; + protected byte _bytes[][] = new byte[1][]; + + XConnectionInputStream_Adapter(XConnection xConnection) { + if(xConnection == null) throw new NullPointerException("the XConnection must not be null"); + + if(DEBUG) System.err.println("#### " + getClass().getName() + " - instantiated "); + + _xConnection = xConnection; + } + + @Override + public int read() throws IOException { + int len; + + try { + len = _xConnection.read(_bytes, 1); + } catch(com.sun.star.io.IOException ioException) { + IOException ex = new IOException(ioException.getMessage()); + ex.initCause(ioException); + throw ex; + } + + if(DEBUG) System.err.println("#### " + getClass().getName() + " - one byte read:" + _bytes[0][0]); + + return len == 0 ? -1 : _bytes[0][0] & 0xff; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + try { + len = _xConnection.read(_bytes, len - off); + } catch(com.sun.star.io.IOException ioException) { + IOException ex = new IOException(ioException.getMessage()); + ex.initCause(ioException); + throw ex; + } + + System.arraycopy(_bytes[0], 0, b, off, len); + + return len == 0 ? -1 : len; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionOutputStream_Adapter.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionOutputStream_Adapter.java new file mode 100644 index 000000000..ac198f8fd --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/XConnectionOutputStream_Adapter.java @@ -0,0 +1,89 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.bridges.java_remote; + + +import java.io.IOException; +import java.io.OutputStream; + +import com.sun.star.connection.XConnection; + + +class XConnectionOutputStream_Adapter extends OutputStream { + private static final boolean DEBUG = false; + + protected XConnection _xConnection; + protected byte _bytes[] = new byte[1]; + + XConnectionOutputStream_Adapter(XConnection xConnection) { + if(DEBUG) System.err.println("#### " + this.getClass() + " - instantiated "); + + _xConnection = xConnection; + } + + @Override + public void write(int b) throws IOException { + _bytes[0] = (byte)b; + + try { + _xConnection.write(_bytes); + } catch(com.sun.star.io.IOException ioException) { + IOException ex = new IOException(ioException.getMessage()); + ex.initCause(ioException); + throw ex; + } + + if(DEBUG) System.err.println("#### " + this.getClass() + " - one byte written:" + _bytes[0]); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + byte bytes[] ; + + if(off == 0 && len == b.length) { + bytes = b; + } else { + bytes = new byte[len]; + + System.arraycopy(b, off, bytes, 0, len); + } + + try { + _xConnection.write(bytes); + } catch(com.sun.star.io.IOException ioException) { + IOException ex = new IOException(ioException.getMessage()); + ex.initCause(ioException); + throw ex; + } + } + + @Override + public void flush() throws IOException { + try { + _xConnection.flush(); + } catch(com.sun.star.io.IOException ioException) { + IOException ex = new IOException(ioException.getMessage()); + ex.initCause(ioException); + throw ex; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java b/ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java new file mode 100644 index 000000000..392e031e3 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java @@ -0,0 +1,700 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.bridges.java_remote; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import com.sun.star.bridge.XBridge; +import com.sun.star.bridge.XInstanceProvider; +import com.sun.star.connection.XConnection; +import com.sun.star.lang.DisposedException; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; +import com.sun.star.lib.uno.environments.java.java_environment; +import com.sun.star.lib.uno.environments.remote.IProtocol; +import com.sun.star.lib.uno.environments.remote.IReceiver; +import com.sun.star.lib.uno.environments.remote.IThreadPool; +import com.sun.star.lib.uno.environments.remote.Job; +import com.sun.star.lib.uno.environments.remote.Message; +import com.sun.star.lib.uno.environments.remote.ThreadId; +import com.sun.star.lib.uno.environments.remote.ThreadPoolManager; +import com.sun.star.lib.uno.typedesc.MethodDescription; +import com.sun.star.lib.uno.typedesc.TypeDescription; +import com.sun.star.lib.util.DisposeListener; +import com.sun.star.lib.util.DisposeNotifier; +import com.sun.star.uno.Any; +import com.sun.star.uno.IBridge; +import com.sun.star.uno.IEnvironment; +import com.sun.star.uno.Type; +import com.sun.star.uno.TypeClass; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; + +/** + * This class implements a remote bridge. + * + * <p>Therefore various interfaces are implemented.</p> + * + * <p>The protocol to used is passed by name, the bridge + * then looks for it under <code>com.sun.star.lib.uno.protocols</code>.</p> + * + * @since UDK1.0 + */ +public class java_remote_bridge + implements IBridge, IReceiver, RequestHandler, XBridge, XComponent, + DisposeNotifier +{ + /** + * When set to true, enables various debugging output. + */ + private static final boolean DEBUG = false; + + private final class MessageDispatcher extends Thread { + public MessageDispatcher() { + super("MessageDispatcher"); + } + + @Override + public void run() { + try { + for (;;) { + synchronized (this) { + if (terminate) { + break; + } + } + Message msg = _iProtocol.readMessage(); + Object obj = null; + if (msg.isRequest()) { + String oid = msg.getObjectId(); + Type type = new Type(msg.getType()); + int fid = msg.getMethod().getIndex(); + if (fid == MethodDescription.ID_RELEASE) { + _java_environment.revokeInterface(oid, type); + remRefHolder(type, oid); + if (msg.isSynchronous()) { + sendReply(false, msg.getThreadId(), null); + } + continue; + } + obj = _java_environment.getRegisteredInterface( + oid, type); + if (obj == null + && fid == MethodDescription.ID_QUERY_INTERFACE) + { + if (_xInstanceProvider == null) { + sendReply( + true, msg.getThreadId(), + new com.sun.star.uno.RuntimeException( + "unknown OID " + oid)); + continue; + } else { + UnoRuntime.setCurrentContext( + msg.getCurrentContext()); + try { + obj = _xInstanceProvider.getInstance(oid); + } catch (com.sun.star.uno.RuntimeException e) { + sendReply(true, msg.getThreadId(), e); + continue; + } catch (Exception e) { + sendReply( + true, msg.getThreadId(), + new com.sun.star.uno.RuntimeException( + e.toString())); + continue; + } finally { + UnoRuntime.setCurrentContext(null); + } + } + } + } + _iThreadPool.putJob( + new Job(obj, java_remote_bridge.this, msg)); + } + } catch (Throwable e) { + dispose(e); + } + } + + public synchronized void terminate() { + terminate = true; + } + + private boolean terminate = false; + } + + protected XConnection _xConnection; + + protected XInstanceProvider _xInstanceProvider; + + protected String _name = "remote"; + private final String protocol; + protected IProtocol _iProtocol; + protected IEnvironment _java_environment; + protected MessageDispatcher _messageDispatcher; + protected final AtomicInteger _life_count = new AtomicInteger(); // determines if this bridge is alive, which is controlled by acquire and release calls + + private final ArrayList<XEventListener> _listeners = new ArrayList<XEventListener>(); + + protected IThreadPool _iThreadPool; + + // Variable disposed must only be used while synchronized on this object: + private boolean disposed = false; + + /** + * This method is for testing only. + */ + int getLifeCount() { + return _life_count.get(); + } + + /** + * This method is for testing only. + */ + IProtocol getProtocol() { + return _iProtocol; + } + + /** + * The ref holder stuff strongly holds objects mapped out via this bridge + * (the java_environment only holds them weakly). + * + * <p>When this bridge is disposed, all remaining ref holder entries are + * released.</p> + */ + private static final class RefHolder { + public RefHolder(Type type, Object object) { + this.type = type; + this.object = object; + } + + public Type getType() { + return type; + } + + public void acquire() { + ++count; + } + + public boolean release() { + return --count == 0; + } + + private final Type type; + @SuppressWarnings("unused") + private final Object object; + private int count = 1; + } + + private final HashMap<String, LinkedList<RefHolder>> refHolders = new HashMap<String, LinkedList<RefHolder>>(); + // from OID (String) to LinkedList of RefHolder + + private boolean hasRefHolder(String oid, Type type) { + synchronized (refHolders) { + LinkedList<RefHolder> l = refHolders.get(oid); + if (l != null) { + for (RefHolder rh : l) { + if (type.isSupertypeOf(rh.getType())) { + return true; + } + } + } + } + return false; + } + + final void addRefHolder(Object obj, Type type, String oid) { + synchronized (refHolders) { + LinkedList<RefHolder> l = refHolders.get(oid); + if (l == null) { + l = new LinkedList<RefHolder>(); + refHolders.put(oid, l); + } + boolean found = false; + for (Iterator<RefHolder> i = l.iterator(); !found && i.hasNext();) { + RefHolder rh = i.next(); + if (rh.getType().equals(type)) { + found = true; + rh.acquire(); + } + } + if (!found) { + l.add(new RefHolder(type, obj)); + } + } + acquire(); + } + + final void remRefHolder(Type type, String oid) { + synchronized (refHolders) { + LinkedList<RefHolder> l = refHolders.get(oid); + if (l == null) { + return; + } + for (RefHolder rh : l) { + if (rh.getType().equals(type)) { + try { + if (rh.release()) { + l.remove(rh); + if (l.isEmpty()) { + refHolders.remove(oid); + } + } + } finally { + release(); + } + break; + } + } + } + } + + final void freeHolders() { + synchronized (refHolders) { + for (Iterator<Map.Entry<String,LinkedList<RefHolder>>> i1 = refHolders.entrySet().iterator(); i1.hasNext();) + { + Map.Entry<String,LinkedList<RefHolder>> e = i1.next(); + String oid = e.getKey(); + LinkedList<RefHolder> l = e.getValue(); + for (Iterator<RefHolder> i2 = l.iterator(); i2.hasNext();) { + RefHolder rh = i2.next(); + for (boolean done = false; !done;) { + done = rh.release(); + _java_environment.revokeInterface(oid, rh.getType()); + release(); + } + } + } + refHolders.clear(); + } + } + + public java_remote_bridge( + IEnvironment java_environment, IEnvironment remote_environment, + Object[] args) + throws Exception + { + _java_environment = java_environment; + String proto = (String) args[0]; + _xConnection = (XConnection) args[1]; + _xInstanceProvider = (XInstanceProvider) args[2]; + if (args.length > 3) { + _name = (String) args[3]; + } + String attr; + int i = proto.indexOf(','); + if (i >= 0) { + protocol = proto.substring(0, i); + attr = proto.substring(i + 1); + } else { + protocol = proto; + attr = null; + } + _iProtocol = (IProtocol) Class.forName( + "com.sun.star.lib.uno.protocols." + protocol + "." + protocol). + getConstructor( + new Class[] { + IBridge.class, String.class, InputStream.class, + OutputStream.class }). + newInstance( + new Object[] { + this, attr, + new XConnectionInputStream_Adapter(_xConnection), + new XConnectionOutputStream_Adapter(_xConnection) }); + proxyFactory = new ProxyFactory(this, this); + _iThreadPool = ThreadPoolManager.create(); + _messageDispatcher = new MessageDispatcher(); + _messageDispatcher.start(); + _iProtocol.init(); + } + + private void notifyListeners() { + EventObject eventObject = new EventObject(this); + + Iterator<XEventListener> elements = _listeners.iterator(); + while(elements.hasNext()) { + XEventListener xEventListener = elements.next(); + + try { + xEventListener.disposing(eventObject); + } + catch(com.sun.star.uno.RuntimeException runtimeException) { + // we are here not interested in any exceptions + } + } + } + + /** + * Constructs a new bridge. + * <p> This method is not part of the provided <code>api</code> + * and should only be used by the UNO runtime.</p> + * + * @param args the custom parameters: arg[0] == protocol_name, + * arg[1] == xConnection, arg[2] == xInstanceProvider. + * + * @deprecated as of UDK 1.0 + */ + @Deprecated + public java_remote_bridge(Object args[]) throws Exception { + this(UnoRuntime.getEnvironment("java", null), UnoRuntime.getEnvironment("remote", null), args); + } + + /** + * + * @see com.sun.star.uno.IBridge#mapInterfaceTo + */ + public Object mapInterfaceTo(Object object, Type type) { + checkDisposed(); + if (object == null) { + return null; + } else { + String[] oid = new String[1]; + object = _java_environment.registerInterface(object, oid, type); + if (!proxyFactory.isProxy(object)) { + // This branch must be taken iff object either is no proxy at + // all or a proxy from some other bridge. There are objects + // that behave like objects for this bridge but that are not + // detected as such by proxyFactory.isProxy. The only known + // case of such objects is com.sun.star.comp.beans.Wrapper, + // which implements com.sun.star.lib.uno.Proxy and effectively + // is a second proxy around a proxy that can be from this + // bridge. For that case, there is no problem, however: Since + // the proxies generated by ProxyFactory send each + // queryInterface to the original object (i.e., they do not + // short-circuit requests for a super-interface to themselves), + // there will always be an appropriate ProxyFactory-proxy + // registered at the _java_environment, so that the object + // returned by _java_environment.registerInterface will never be + // a com.sun.star.comp.beans.Wrapper. + addRefHolder(object, type, oid[0]); + } + return oid[0]; + } + } + + /** + * Maps an object from destination environment to the source environment. + * + * @param oId the object to map. + * @param type the interface under which is to be mapped. + * @return the object in the source environment. + * + * @see com.sun.star.uno.IBridge#mapInterfaceFrom + */ + public Object mapInterfaceFrom(Object oId, Type type) { + checkDisposed(); + // TODO What happens if an exception is thrown after the call to + // acquire, but before it is guaranteed that a pairing release will be + // called eventually? + acquire(); + String oid = (String) oId; + Object object = _java_environment.getRegisteredInterface(oid, type); + if (object == null) { + object = _java_environment.registerInterface( + proxyFactory.create(oid, type), new String[] { oid }, type); + // the proxy sends a release when finalized + } else if (!hasRefHolder(oid, type)) { + sendInternalRequest(oid, type, "release", null); + } + return object; + } + + /** + * Gives the source environment. + * + * @return the source environment of this bridge. + * @see com.sun.star.uno.IBridge#getSourceEnvironment + */ + public IEnvironment getSourceEnvironment() { + return _java_environment; + } + + /** + * Gives the destination environment. + * + * @return the destination environment of this bridge. + * @see com.sun.star.uno.IBridge#getTargetEnvironment + */ + public IEnvironment getTargetEnvironment() { + return null; + } + + /** + * Increases the life count. + * + * @see com.sun.star.uno.IBridge#acquire + */ + public void acquire() { + if(DEBUG) { + int x = _life_count.incrementAndGet(); + System.err.println("##### " + getClass().getName() + ".acquire:" + x); + } else { + _life_count.incrementAndGet(); + } + } + + /** + * Decreases the life count. + * + * <p>If the life count drops to zero, the bridge disposes itself.</p> + * + * @see com.sun.star.uno.IBridge#release + */ + public void release() { + int x = _life_count.decrementAndGet(); + if (x <= 0) { + dispose(new Throwable("end of life")); + } + } + + public void dispose() { + dispose(new Throwable("user dispose")); + } + + private void dispose(Throwable throwable) { + synchronized (this) { + if (disposed) { + return; + } + disposed = true; + } + + notifyListeners(); + for (Iterator<DisposeListener> i = disposeListeners.iterator(); i.hasNext();) { + i.next().notifyDispose(this); + } + + _iProtocol.terminate(); + + try { + _messageDispatcher.terminate(); + + try { + _xConnection.close(); + } catch (com.sun.star.io.IOException e) { + System.err.println( + getClass().getName() + ".dispose - IOException:" + e); + } + + if (Thread.currentThread() != _messageDispatcher + && _messageDispatcher.isAlive()) + { + _messageDispatcher.join(1000); + if (_messageDispatcher.isAlive()) { + _messageDispatcher.interrupt(); + _messageDispatcher.join(); + } + } + + // interrupt all jobs queued by this bridge + _iThreadPool.dispose(throwable); + + // release all out-mapped objects and all in-mapped proxies: + freeHolders(); + // assert _java_environment instanceof java_environment; + ((java_environment) _java_environment).revokeAllProxies(); + + proxyFactory.dispose(); + + if (DEBUG) { + if (_life_count.get() != 0) { + System.err.println(getClass().getName() + + ".dispose - life count (proxies left):" + + _life_count); + } + _java_environment.list(); + } + + // clear members + _xConnection = null; + _java_environment = null; + _messageDispatcher = null; + } catch (InterruptedException e) { + System.err.println(getClass().getName() + + ".dispose - InterruptedException:" + e); + } + } + + /** + * + * @see com.sun.star.bridge.XBridge#getInstance + */ + public Object getInstance(String instanceName) { + Type t = new Type(XInterface.class); + return sendInternalRequest( + instanceName, t, "queryInterface", new Object[] { t }); + } + + /** + * Gives the name of this bridge. + * + * @return the name of this bridge. + * @see com.sun.star.bridge.XBridge#getName + */ + public String getName() { + return _name; + } + + /** + * Gives a description of the connection type and protocol used. + * + * @return connection type and protocol. + * @see com.sun.star.bridge.XBridge#getDescription + */ + public String getDescription() { + return protocol + "," + _xConnection.getDescription(); + } + + public void sendReply(boolean exception, ThreadId threadId, Object result) { + if (DEBUG) { + System.err.println("##### " + getClass().getName() + ".sendReply: " + + exception + " " + result); + } + + checkDisposed(); + + try { + _iProtocol.writeReply(exception, threadId, result); + } catch (IOException e) { + dispose(e); + throw (DisposedException) + (new DisposedException("unexpected " + e).initCause(e)); + } catch (RuntimeException e) { + dispose(e); + throw e; + } catch (Error e) { + dispose(e); + throw e; + } + } + + public Object sendRequest( + String oid, Type type, String operation, Object[] params) + throws Throwable + { + Object result = null; + + checkDisposed(); + + ThreadId threadId = _iThreadPool.getThreadId(); + Object handle = _iThreadPool.attach(threadId); + try { + boolean sync; + try { + sync = _iProtocol.writeRequest( + oid, TypeDescription.getTypeDescription(type), operation, + threadId, params); + } catch (IOException e) { + dispose(e); + throw (DisposedException) + new DisposedException(e.toString()).initCause(e); + } + if (sync && Thread.currentThread() != _messageDispatcher) { + result = _iThreadPool.enter(handle, threadId); + } + } finally { + _iThreadPool.detach(handle, threadId); + if(operation.equals("release")) + release(); // kill this bridge, if this was the last proxy + } + + if(DEBUG) System.err.println("##### " + getClass().getName() + ".sendRequest left:" + result); + + // On the wire (at least in URP), the result of queryInterface is + // transported as an ANY, but in Java it shall be transported as a + // direct reference to the UNO object (represented as a Java Object), + // never boxed in a com.sun.star.uno.Any: + if (operation.equals("queryInterface") && result instanceof Any) { + Any a = (Any) result; + if (a.getType().getTypeClass() == TypeClass.INTERFACE) { + result = a.getObject(); + } else { + result = null; // should never happen + } + } + + return result; + } + + private Object sendInternalRequest( + String oid, Type type, String operation, Object[] arguments) + { + try { + return sendRequest(oid, type, operation, arguments); + } catch (Error e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException("Unexpected " + e); + } + } + + /** + * Methods XComponent. + */ + public void addEventListener(XEventListener xEventListener) { + _listeners.add(xEventListener); + } + + public void removeEventListener(XEventListener xEventListener) { + _listeners.remove(xEventListener); + } + + /** + * + * @see DisposeNotifier#addDisposeListener + */ + public void addDisposeListener(DisposeListener listener) { + synchronized (this) { + if (!disposed) { + disposeListeners.add(listener); + return; + } + } + listener.notifyDispose(this); + } + + /** + * This function must only be called while synchronized on this object. + */ + private synchronized void checkDisposed() { + if (disposed) { + throw new DisposedException("java_remote_bridge " + this + + " is disposed"); + } + } + + private final ProxyFactory proxyFactory; + + // Access to disposeListeners must be synchronized on <CODE>this</CODE>: + private final ArrayList<DisposeListener> disposeListeners = new ArrayList<DisposeListener>(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/java/java_environment.java b/ridljar/com/sun/star/lib/uno/environments/java/java_environment.java new file mode 100644 index 000000000..89e4b1088 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/java/java_environment.java @@ -0,0 +1,312 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.java; + +import com.sun.star.uno.IEnvironment; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Iterator; + +/** + * The java_environment is the environment where objects and + * interfaces are registered, which are mapped out of java or + * into java. + * + * <p>The java_environment implements the <code>IEnvironment</code> interface + * defined in the uno runtime.</p> + * + * @see com.sun.star.uno.UnoRuntime + * @see com.sun.star.uno.IEnvironment + * @since UDK1.0 + */ +public final class java_environment implements IEnvironment { + public java_environment(Object context) { + this.context = context; + } + + /** + * + * @see com.sun.star.uno.IEnvironment#getContext + */ + public Object getContext() { + return context; + } + + /** + * + * @see com.sun.star.uno.IEnvironment#getName + */ + public String getName() { + return "java"; + } + + /** + * + * @see com.sun.star.uno.IEnvironment#registerInterface + */ + public Object registerInterface(Object object, String[] oid, Type type) { + if (oid[0] == null) { + oid[0] = UnoRuntime.generateOid(object); + } + return (isProxy(object) ? proxies : localObjects).register( + object, oid[0], type); + } + + /** + * You have to revoke ANY interface that has been registered via this + * method. + * + * @param oid object id of interface to be revoked. + * @param type the type description of the interface. + * @see com.sun.star.uno.IEnvironment#revokeInterface + */ + public void revokeInterface(String oid, Type type) { + if (!proxies.revoke(oid, type)) { + localObjects.revoke(oid, type); + } + } + + /** + * Retrieves an interface identified by its object id and type from this + * environment. + * + * @param oid object id of interface to be retrieved. + * @param type the type description of the interface to be retrieved. + * @see com.sun.star.uno.IEnvironment#getRegisteredInterface + */ + public Object getRegisteredInterface(String oid, Type type) { + Object o = proxies.get(oid, type); + if (o == null) { + o = localObjects.get(oid, type); + } + return o; + } + + /** + * Retrieves the object identifier for a registered interface from this + * environment. + * + * @param object a registered interface. + * @see com.sun.star.uno.IEnvironment#getRegisteredObjectIdentifier + */ + public String getRegisteredObjectIdentifier(Object object) { + return UnoRuntime.generateOid(object); + } + + /** + * + * @see com.sun.star.uno.IEnvironment#list + */ + public void list() { +// TODO??? + } + + /** + * Revokes all registered proxy interfaces. + * + * <p>This method should be part of <code>IEnvironment</code>. It is called + * from <code>com.sun.star.lib.uno.bridges.java_remote.<!-- + * -->java_remote_bridge.dispose</code>.</p> + */ + public void revokeAllProxies() { + proxies.clear(); + } + + // TODO What's this??? java.lang.Object#equals requires reflexivity... + // + // Maybe this was hacked in so that different bridges use different + // instances of java_environment. That is desirable for the following + // reason: An OID is bridged in over bridge A, a proxy is created on the + // Java side, and recorded in the java_environment. The same OID is then + // bridged in over another bridge B. If there were only one + // java_environment shared by both bridges, the proxy from bridge A would be + // reused. If now bridge A is taken down programmatically (e.g., because + // some controlling code somehow deduced that no objects are mapped over + // that bridge any longer), but the proxy is still used by bridge B, using + // the proxy would now result in errors. The explicit API to control + // bridges forbids to transparently share proxies between bridges, and using + // different java_environment instances for different bridges is the way to + // enforce this. + @Override + public boolean equals(Object obj) { + return false; + } + + private static final class Registry { + public synchronized Object register( + Object object, String oid, Type type) + { + cleanUp(); + Level1Entry l1 = level1map.get(oid); + if (l1 != null) { + Level2Entry l2 = l1.level2map.get(type); + if (l2 != null) { + Object o = l2.get(); + if (o != null) { + l2.acquire(); + return o; + } + } + } + // TODO If a holder references an unreachable object, but still has + // a positive count, it is replaced with a new holder (referencing a + // reachable object, and with a count of 1). Any later calls to + // revoke that should decrement the count of the previous holder + // would now decrement the count of the new holder, removing it + // prematurely. This is a design flaw that will be fixed when + // IEnvironment.revokeInterface is changed to no longer use + // counting. (And this problem is harmless, as currently a holder + // either references a strongly held object and uses register/revoke + // to control it, or references a weakly held proxy and never + // revokes it.) + if (l1 == null) { + l1 = new Level1Entry(); + level1map.put(oid, l1); + } + l1.level2map.put(type, new Level2Entry(oid, type, object, queue)); + return object; + } + + public synchronized boolean revoke(String oid, Type type) { + Level1Entry l1 = level1map.get(oid); + Level2Entry l2 = null; + if (l1 != null) { + l2 = l1.level2map.get(type); + if (l2 != null && l2.release()) { + removeLevel2Entry(l1, oid, type); + } + } + cleanUp(); + return l2 != null; + } + + public synchronized Object get(String oid, Type type) { + Level1Entry l1 = level1map.get(oid); + return l1 == null ? null : l1.find(type); + } + + public synchronized void clear() { + level1map.clear(); + cleanUp(); + } + + // must only be called while synchronized on this Registry: + private void cleanUp() { + for (;;) { + Object tmp = queue.poll(); + Level2Entry l2 = (Level2Entry) tmp; + if (l2 == null) { + break; + } + // It is possible that a Level2Entry e1 for the OID/type pair + // (o,t) becomes weakly reachable, then another Level2Entry e2 + // is registered for the same pair (o,t) (a new Level2Entry is + // created since now e1.get() == null), and only then e1 is + // enqueued. To not erroneously remove the new e2 in that case, + // check whether the map still contains e1: + Level1Entry l1 = level1map.get(l2.oid); + if (l1 != null && l1.level2map.get(l2.type) == l2) { + removeLevel2Entry(l1, l2.oid, l2.type); + } + } + } + + // must only be called while synchronized on this Registry: + private void removeLevel2Entry(Level1Entry l1, String oid, Type type) { + l1.level2map.remove(type); + if (l1.level2map.isEmpty()) { + level1map.remove(oid); + } + } + + private static final class Level1Entry { + // must only be called while synchronized on enclosing Registry: + public Object find(Type type) { + // First, look for an exactly matching entry; then, look for an + // arbitrary entry for a subtype of the request type: + Level2Entry l2 = level2map.get(type); + if (l2 != null) { + Object o = l2.get(); + if (o != null) { + return o; + } + } + for (Iterator<Level2Entry> i = level2map.values().iterator(); + i.hasNext();) + { + l2 = i.next(); + if (type.isSupertypeOf(l2.type)) { + Object o = l2.get(); + if (o != null) { + return o; + } + } + } + return null; + } + + public final HashMap<Type, Level2Entry> level2map = + new HashMap<Type, Level2Entry>(); + } + + private static final class Level2Entry extends WeakReference<Object> { + public Level2Entry( + String oid, Type type, Object object, ReferenceQueue<Object> queue) + { + super(object, queue); + this.oid = oid; + this.type = type; + } + + // must only be called while synchronized on enclosing Registry: + public void acquire() { + ++count; + } + + // must only be called while synchronized on enclosing Registry: + public boolean release() { + return --count == 0; + } + + public final String oid; + public final Type type; + + private int count = 1; + } + + private final HashMap<String, Level1Entry> level1map = + new HashMap<String, Level1Entry>(); + private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); + } + + private boolean isProxy(Object object) { + return object instanceof com.sun.star.lib.uno.Proxy; + } + + private static final Registry localObjects = new Registry(); + + private final Object context; + private final Registry proxies = new Registry(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/IProtocol.java b/ridljar/com/sun/star/lib/uno/environments/remote/IProtocol.java new file mode 100644 index 000000000..c2ecbf9a0 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/IProtocol.java @@ -0,0 +1,93 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +import com.sun.star.lib.uno.typedesc.TypeDescription; +import java.io.IOException; + +/** + * An abstraction of remote bridge protocols. + * + * <p>A class implementing a given protocol <var>prot</var> must be named + * <code>com.sun.star.lib.uno.protocols.<var>prot</var>.<var>prot</var></code> + * and must have a public constructor that takes four arguments: The first + * argument of type <code>com.sun.star.uno.IBridge</code> must not be null. The + * second argument of type <code>String</code> represents any attributes; it may + * be null if there are no attributes. The third argument of type + * <code>java.io.InputStream</code> must not be null. The fourth argument of + * type <code>java.io.OutputStream</code> must not be null.</p> + */ +public interface IProtocol { + /** + * Initializes the connection. + * + * <p>This method must be called exactly once, after the + * <code>readMessage</code> loop has already been established.</p> + */ + void init() throws IOException; + + void terminate(); + + /** + * Reads a request or reply message. + * + * <p>Access to this method from multiple threads must be properly + * synchronized.</p> + * + * @return a non-null message; if the input stream is exhausted, a + * <code>java.io.IOException</code> is thrown instead. + */ + Message readMessage() throws IOException; + + /** + * Writes a request message. + * + * @param oid a non-null OID. + * @param type a non-null UNO type. + * @param function a non-null function (the name of a UNO interface method + * or attribute compatible with the given <code>type</code>, or either + * <code>"queryInterface"</code> or <code>"release"</code>). + * @param tid a non-null TID. + * @param arguments a list of UNO arguments compatible with the given + * <code>type</code> and <code>function</code>; may be null to represent + * an empty list. + * @return <code>true</code> if the request message is sent as a synchronous + * request. + */ + boolean writeRequest( + String oid, TypeDescription type, String function, ThreadId tid, + Object[] arguments) + throws IOException; + + /** + * Writes a reply message. + * + * @param exception <code>true</code> if the reply corresponds to a raised + * exception. + * @param tid a non-null TID. + * @param result if <code>exception</code> is <code>true</code>, a non-null + * UNO exception; otherwise, a UNO return value, which may be null to + * represent a <code>VOID</code> return value. + */ + void writeReply(boolean exception, ThreadId tid, Object result) + throws IOException; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/IReceiver.java b/ridljar/com/sun/star/lib/uno/environments/remote/IReceiver.java new file mode 100644 index 000000000..e39ae3d4d --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/IReceiver.java @@ -0,0 +1,40 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +/** + * An abstraction for giving back a reply for a request. + * + * @see com.sun.star.uno.IQueryInterface + */ +public interface IReceiver { + /** + * Send back a reply for a request. + * + * @param exception <CODE>true</CODE> if an exception (instead of a normal + * result) is sent back. + * @param threadId the thread ID of the request. + * @param result the result of executing the request, or an exception thrown + * while executing the request. + */ + void sendReply(boolean exception, ThreadId threadId, Object result); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/IThreadPool.java b/ridljar/com/sun/star/lib/uno/environments/remote/IThreadPool.java new file mode 100644 index 000000000..1de31ad04 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/IThreadPool.java @@ -0,0 +1,115 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +/** + * This interface is an abstraction of the various threadpool implementations. + * + * @see com.sun.star.lib.uno.environments.remote.ThreadPoolManager + * @since UDK1.0 + */ +public interface IThreadPool { + /** + * Retrieves the global threadId for the current thread. + * + * @return the thread id. + */ + ThreadId getThreadId(); + + /** + * Attaches this thread to the thread pool. + * + * @see #enter + */ + void attach(); + + /** + * As above, but hands in an already existing instance of the threadid of + * the current thread. + * + * <p>The function exists for performance.</p> + * + * @return Returns a handle which can be used in enter and detach calls. + * @see #attach + */ + Object attach( ThreadId id ); + + /** + * Detaches this thread from the thread pool. + * @see #enter + */ + void detach(); + + /** + * As above, but hands in an already existing instance of the threadid of + * the current thread and a handle returned by attach. + * + * <p>The function exists for performance.</p> + * + * @see #attach() + * @see #detach() + */ + void detach( Object handle, ThreadId id ); + + /** + * Lets this thread enter the thread pool. + * + * <p>This thread then executes all jobs put via <code>putJob</code> until + * a reply job arrives.</p> + * + * @see #putJob + */ + Object enter() throws Throwable; + + /** + * As above but hands in an already existing instance of the threadid of + * the current thread and a handle returned by attach. + * + * <p>This thread then executes all jobs put via <code>putJob</code> until + * a reply job arrives.</p> + * + * @see #putJob + */ + Object enter( Object handle, ThreadId id ) throws Throwable; + + /** + * Queues a job into the jobQueue of the thread belonging to the jobs + * threadId. + * + * @param job the job + */ + void putJob(Job job); + + /** + * Disposes this thread pool, thus releasing all threads by throwing a + * <code>DisposedException</code> with the given <code>Throwable</code> cause. + * + * @param throwable the cause + */ + void dispose(Throwable throwable); + + + /** + * Destroys the thread pool and tries to join all created threads immediately. + */ + void destroy(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPool.java b/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPool.java new file mode 100644 index 000000000..332306be0 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPool.java @@ -0,0 +1,124 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +/** + * This class implements a java thread pool. + * + * @see com.sun.star.uno.UnoRuntime + * @see com.sun.star.lib.uno.environments.remote.NativeThreadPool + * @see com.sun.star.lib.uno.environments.remote.IThreadPool + * @see com.sun.star.lib.uno.environments.remote.Job + * @see com.sun.star.lib.uno.environments.remote.JobQueue + * @since UDK1.0 + */ +public class JavaThreadPool implements IThreadPool { + /** + * When set to true, enables various debugging output. + */ + private static final boolean DEBUG = false; + + JavaThreadPoolFactory _javaThreadPoolFactory; + + JavaThreadPool(JavaThreadPoolFactory javaThreadPoolFactory) { + _javaThreadPoolFactory = javaThreadPoolFactory; + } + + public ThreadId getThreadId() { + return JavaThreadPoolFactory.getThreadId(); + } + + public Object attach( ThreadId threadId ) + { + if(DEBUG) System.err.println("##### " + getClass().getName() + ".attach - id:" + threadId); + JobQueue jobQueue = _javaThreadPoolFactory.getJobQueue(threadId); + if(jobQueue == null) + jobQueue = new JobQueue(_javaThreadPoolFactory, threadId, false); + + // acquiring the jobQueue registers it at the ThreadPoolFactory + jobQueue.acquire(); + return jobQueue; + } + + public void attach() { + attach( getThreadId() ); + } + + public void detach( Object handle, ThreadId id ) + { + ((JobQueue)handle).release(); + } + + public void detach() { + ThreadId threadId = getThreadId(); + detach(_javaThreadPoolFactory.getJobQueue(threadId), threadId ); + } + + + public Object enter( ) throws Throwable { + ThreadId threadId = getThreadId(); + return enter( _javaThreadPoolFactory.getJobQueue( threadId ), threadId ); + } + + public Object enter( Object handle, ThreadId threadId ) throws Throwable { + return ((JobQueue)handle).enter(this); + } + + public void putJob(Job job) { + if (!job.isRequest() || job.isSynchronous()) { + JobQueue jobQueue = _javaThreadPoolFactory.getJobQueue(job.getThreadId()); + + // this has not be synchronized, cause + // sync jobs can only come over one bridge + // (cause the thread blocks on other side) + if(jobQueue == null) + jobQueue = new JobQueue(_javaThreadPoolFactory, job.getThreadId(), true); + + // put job acquires the queue and registers it at the ThreadPoolFactory + jobQueue.putJob(job, this); + } + else { + // this has to be synchronized, cause + // async jobs of the same thread can come + // over different bridges + synchronized(_javaThreadPoolFactory) { + JobQueue async_jobQueue = _javaThreadPoolFactory.getAsyncJobQueue(job.getThreadId()); + + // ensure there is jobQueue + if(async_jobQueue == null) // so, there is really no async queue + async_jobQueue = new JobQueue(_javaThreadPoolFactory, job.getThreadId()); + + // put job acquires the queue and registers it at the ThreadPoolFactory + async_jobQueue.putJob(job, this); + } + } + } + + public void dispose(Throwable throwable) { + if(DEBUG) System.err.println("##### " + getClass().getName() + ".dispose:" + throwable); + + _javaThreadPoolFactory.dispose(this, throwable); + } + + public void destroy() { + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPoolFactory.java b/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPoolFactory.java new file mode 100644 index 000000000..181a3e17e --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/JavaThreadPoolFactory.java @@ -0,0 +1,87 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +import java.util.Collection; +import java.util.HashMap; +import java.util.WeakHashMap; + +final class JavaThreadPoolFactory { + + public IThreadPool createThreadPool() { + return new JavaThreadPool(this); + } + + public void addJobQueue(JobQueue jobQueue) { + synchronized (jobQueues) { + jobQueues.put(jobQueue.getThreadId(), jobQueue); + } + } + + public void removeJobQueue(JobQueue jobQueue) { + synchronized (jobQueues) { + jobQueues.remove(jobQueue.getThreadId()); + } + } + + public JobQueue getJobQueue(ThreadId threadId) { + synchronized (jobQueues) { + return jobQueues.get(threadId); + } + } + + public JobQueue getAsyncJobQueue(ThreadId threadId) { + JobQueue q = getJobQueue(threadId); + return q == null ? null : q._async_jobQueue; + } + + public void dispose(Object disposeId, Throwable throwable) { + JobQueue[] qs; + synchronized (jobQueues) { + Collection<JobQueue> c = jobQueues.values(); + qs = c.toArray(new JobQueue[c.size()]); + } + for (int i = 0; i < qs.length; ++i) { + qs[i].dispose(disposeId, throwable); + } + } + + public static ThreadId getThreadId() { + Thread t = Thread.currentThread(); + if (t instanceof JobQueue.JobDispatcher) { + return ((JobQueue.JobDispatcher) t).getThreadId(); + } else { + ThreadId id; + synchronized (threadIdMap) { + id = threadIdMap.get(t); + if (id == null) { + id = ThreadId.createFresh(); + threadIdMap.put(t, id); + } + } + return id; + } + } + + private static final WeakHashMap<Thread, ThreadId> threadIdMap = new WeakHashMap<Thread, ThreadId>(); + private final HashMap<ThreadId, JobQueue> jobQueues = new HashMap<ThreadId, JobQueue>(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/Job.java b/ridljar/com/sun/star/lib/uno/environments/remote/Job.java new file mode 100644 index 000000000..8ec4492c4 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/Job.java @@ -0,0 +1,163 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + + +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.lang.reflect.InvocationTargetException; + +import com.sun.star.lib.uno.typedesc.MethodDescription; +import com.sun.star.uno.Any; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XCurrentContext; + +/** + * The Job is an abstraction for tasks which have to be done + * remotely because of a method invocation. + * + * @see com.sun.star.lib.uno.environments.remote.ThreadId + * @see com.sun.star.lib.uno.environments.remote.IReceiver + * @since UDK1.0 + */ +public class Job { + protected IReceiver _iReceiver; + protected Message _iMessage; + Object _disposeId; + + protected Object _object; + + public Job(Object object, IReceiver iReceiver, Message iMessage) { + _object = object; + _iReceiver = iReceiver; + _iMessage = iMessage; + } + + /** + * Dispatches a <code>queryInterface</code> call. + * + * @return the result of the call (should be an <code>Any</code>). + */ + protected Object dispatch_queryInterface(Type type) { + Class<?> zInterface = type.getTypeDescription().getZClass(); + + Object result = null; + + Object face = UnoRuntime.queryInterface(zInterface, _object); + // the hell knows why, but empty interfaces a given back as void anys + if(face != null) + result = new Any(type, face); + return result; + } + + /** + * Execute the job. + * + * @return the result of the message. + */ + public Object execute() throws Throwable { + if (_iMessage.isRequest()) { + Object result = null; + Throwable exception = null; + MethodDescription md = _iMessage.getMethod(); + Object[] args = _iMessage.getArguments(); + XCurrentContext oldCC = UnoRuntime.getCurrentContext(); + UnoRuntime.setCurrentContext(_iMessage.getCurrentContext()); + try { + result = md.getIndex() == MethodDescription.ID_QUERY_INTERFACE + ? dispatch_queryInterface((Type) args[0]) + : md.getMethod().invoke(_object, args); + } catch (InvocationTargetException e) { + exception = e.getCause(); + if (exception == null) { + exception = e; + } + } catch (Exception e) { + exception = e; + } finally { + UnoRuntime.setCurrentContext(oldCC); + } + if (_iMessage.isSynchronous()) { + if (exception == null) { + _iReceiver.sendReply( + false, _iMessage.getThreadId(), result); + } else { + // Here we have to be aware of non-UNO exceptions, because + // they may kill a remote side which does not know anything + // about their types: + if (!(exception instanceof com.sun.star.uno.Exception) + && !(exception instanceof + com.sun.star.uno.RuntimeException)) + { + StringWriter writer = new StringWriter(); + exception.printStackTrace(new PrintWriter(writer)); + exception = new com.sun.star.uno.RuntimeException( + "Java exception: <" + writer + ">", null); + } + _iReceiver.sendReply( + true, _iMessage.getThreadId(), exception); + } + } + return null; + } else if (_iMessage.isAbnormalTermination()) { + throw remoteUnoRequestRaisedException(_iMessage.getResult()); + } else { + return _iMessage.getResult(); + } + } + + public ThreadId getThreadId() { + return _iMessage.getThreadId(); + } + + public boolean isRequest() { + return _iMessage.isRequest(); + } + + public boolean isSynchronous() { + return _iMessage.isSynchronous(); + } + + public void dispose() { +// _oId = null; +// _iReceiver = null; +// _threadId = null; +// _object = null; +// _operation = null; +// _param = null; +// _exception = null; +// _zInterface = null; +// _disposeId = null; + } + + /** + * The name of this method is chosen to generate a somewhat self-explanatory + * stack trace. + */ + private Exception remoteUnoRequestRaisedException(Object exception) { + Exception e = (Exception) exception; + e.fillInStackTrace(); + return e; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/JobQueue.java b/ridljar/com/sun/star/lib/uno/environments/remote/JobQueue.java new file mode 100644 index 000000000..b71525ba5 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/JobQueue.java @@ -0,0 +1,373 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +import java.util.ArrayList; + +import com.sun.star.lang.DisposedException; + +/** + * The <code>JobQueue</code> implements a queue for jobs. + * + * <p>For every jobs thread id exists a job queue which is registered + * at the <code>ThreadPool</code>.</p> + * + * <p>A JobQueue is split into a sync job queue and an async job queue. + * The sync job queue is the registered queue, it delegates async jobs + * (put by <code>putjob</code>) into the async queue, which is only + * known by the sync queue.</p> + * + * @see com.sun.star.lib.uno.environments.remote.IThreadPool + * @see com.sun.star.lib.uno.environments.remote.Job + * @see com.sun.star.lib.uno.environments.remote.ThreadId + * @since UDK1.0 + */ +public class JobQueue { + /** + * When set to true, enables various debugging output. + */ + private static final boolean DEBUG = false; + + final ArrayList<Job> jobList = new ArrayList<Job>(); + + private ThreadId _threadId; // the thread id of the queue + protected int _ref_count = 0; // the stack deepness + private boolean _createThread; // create a worker thread, if needed + private boolean _createThread_now; // create a worker thread, if needed + private Thread _worker_thread; // the thread that does the jobs + + private Object _disposeId; // the active dispose id + private Object _doDispose = null; + private Throwable _throwable; + + JobQueue _async_jobQueue; // chaining job queues for asyncs + protected JobQueue _sync_jobQueue; // chaining job queues for syncs + + private boolean _active = false; + + private JavaThreadPoolFactory _javaThreadPoolFactory; + + /** + * A thread for dispatching jobs. + */ + class JobDispatcher extends Thread { + Object _disposeId; + + JobDispatcher(Object disposeId) { + super("JobDispatcher"); + + if(DEBUG) System.err.println("JobQueue$JobDispatcher.<init>:" + _threadId); + + _disposeId = disposeId; + } + + ThreadId getThreadId() { + return _threadId; + } + + @Override + public void run() { + if(DEBUG) System.err.println("ThreadPool$JobDispatcher.run: " + Thread.currentThread()); + + try { + enter(2000, _disposeId); + } catch(Throwable throwable) { + synchronized (JobQueue.this) { + if(!jobList.isEmpty() || _active) { // there was a job in progress, so give a stack + System.err.println(getClass().getName() + " - exception occurred:" + throwable); + throwable.printStackTrace(System.err); + } + } + } + finally { + release(); + } + + if(DEBUG) System.err.println("##### " + getClass().getName() + ".run - exit:" + _threadId); + } + } + + + /** + * Constructs an async job queue with the given thread id which belongs to + * the given sync job queue. + * + * @param threadId the thread id. + * @see com.sun.star.lib.uno.environments.remote.ThreadId + */ + JobQueue(JavaThreadPoolFactory javaThreadPoolFactory, ThreadId threadId) { + _javaThreadPoolFactory = javaThreadPoolFactory; + _threadId = ThreadId.createFresh(); + + _sync_jobQueue = javaThreadPoolFactory.getJobQueue(threadId); + if(_sync_jobQueue == null) { + _sync_jobQueue = new JobQueue(javaThreadPoolFactory, threadId, true); + _sync_jobQueue.acquire(); + } + + _sync_jobQueue._async_jobQueue = this; + + _createThread = true; + _createThread_now = true; + + acquire(); + + if(DEBUG) System.err.println("##### " + getClass().getName() + " - init:" + _threadId); + } + + /** + * Constructs a sync job queue with the given thread id and the given thread. + * + * @param threadId the thread id. + * @param createThread if true, the queue creates a worker thread if needed. + * @see com.sun.star.lib.uno.environments.remote.ThreadId + */ + JobQueue(JavaThreadPoolFactory javaThreadPoolFactory, ThreadId threadId, boolean createThread){ + _javaThreadPoolFactory = javaThreadPoolFactory; + _threadId = threadId; + _createThread = createThread; + _createThread_now = createThread; + + if(DEBUG) System.err.println("##### " + getClass().getName() + " - init:" + _threadId + " " + createThread); + } + + /** + * Gives the thread id of this queue. + * + * @return the thread id. + * @see com.sun.star.lib.uno.environments.remote.ThreadId + */ + ThreadId getThreadId() { + return _threadId; + } + + synchronized void acquire() { + // add only synchronous queues . + if(_ref_count <= 0 && _sync_jobQueue == null ) + _javaThreadPoolFactory.addJobQueue(this); + + ++ _ref_count; + } + + synchronized void release() { + -- _ref_count; + + if(_ref_count <= 0) { + // only synchronous queues needs to be removed . + if( _sync_jobQueue == null ) + _javaThreadPoolFactory.removeJobQueue(this); + + + if(_sync_jobQueue != null) { + _sync_jobQueue._async_jobQueue = null; + _sync_jobQueue.release(); + } + } + } + + /** + * Removes a job from the queue. + * + * @param waitTime the maximum amount of time to wait for a job. + * @return a job or null if timed out. + */ + private Job removeJob(int waitTime) { + if(DEBUG) System.err.println("##### " + getClass().getName() + ".removeJob:" + jobList + " " + _threadId); + + Job job = null; + synchronized (this) { + // wait max. waitTime time for a job to enter the queue + boolean waited = false; + while(jobList.isEmpty() && (waitTime == 0 || !waited)) { + if(_doDispose == _disposeId) { + _doDispose = null; + throw (DisposedException) + new DisposedException().initCause(_throwable); + } + + // notify sync queues + notifyAll(); + + try { + // wait for new job + wait(waitTime); + } catch(InterruptedException interruptedException) { + throw new com.sun.star.uno.RuntimeException(getClass().getName() + ".removeJob - unexpected:" + interruptedException); + } + + // signal that we have already waited once + waited = true; + } + + + if(!jobList.isEmpty()) { + job = jobList.remove(0); + _active = true; + } + } + + // always wait for asynchron jobqueue to be finished ! + if(job != null && _async_jobQueue != null) { + synchronized(_async_jobQueue) { + // wait for async queue to be empty and last job to be done + while(_async_jobQueue._active || !_async_jobQueue.jobList.isEmpty()) { + if(DEBUG) System.err.println("waiting for async:" + _async_jobQueue.jobList + " " + _async_jobQueue._worker_thread); + + if(_doDispose == _disposeId) { + _doDispose = null; + throw (DisposedException) + new DisposedException().initCause(_throwable); + } + + try { + _async_jobQueue.wait(); + } catch(InterruptedException interruptedException) { + throw new com.sun.star.uno.RuntimeException(getClass().getName() + ".removeJob - unexpected:" + interruptedException); + } + } + } + } + + return job; + } + + /** + * Puts a job into the queue. + * + * @param job the job. + * @param disposeId a dispose id. + */ + synchronized void putJob(Job job, Object disposeId) { + if(DEBUG) System.err.println("##### " + getClass().getName() + ".putJob todoes: " + " job:" + job); + + jobList.add(job); + + if(_worker_thread == null && _createThread && _createThread_now) { // if there is no thread, which dispatches and if shall create one, create one + + acquire(); + + _createThread_now = false; + new JobDispatcher(disposeId).start(); + } + + // always notify possible waiters + notifyAll(); + } + + /** + * Enters the job queue. + * + * @param disposeId a dispose id. + * @return the result of the final job (reply). + */ + Object enter(Object disposeId) throws Throwable { + return enter(0, disposeId); // wait infinitely + } + + /** + * Enters the job queue. + * + * @param waitTime the maximum amount of time to wait for a job (0 means wait infinitely). + * @param disposeId a dispose id. + * @return the result of the final job (reply). + */ + Object enter(int waitTime, Object disposeId) throws Throwable { + if(DEBUG) System.err.println("#####" + getClass().getName() + ".enter: " + _threadId); + + boolean quit = false; + + Object hold_disposeId = _disposeId; + _disposeId = disposeId; + + Object result = null; + + Thread hold_worker_thread = _worker_thread; + _worker_thread = Thread.currentThread(); + + while(!quit) { + Job job = null; + + try { + job = removeJob(waitTime); + + if(job != null) { + try { + result = job.execute(); + } + finally { + _active = false; + } + + if (!job.isRequest()) { + job.dispose(); + + quit = true; + } + + job = null; + } + else + quit = true; + + + } + finally { // ensure that this queue becomes disposed, if necessary + if(DEBUG) System.err.println("##### " + getClass().getName() + ".enter leaving: " + _threadId + " " + _worker_thread + " " + hold_worker_thread + " " + result); + + synchronized(this) { + if(job != null || (quit && jobList.isEmpty())) { + _worker_thread = hold_worker_thread; + + _createThread_now = true; + + _disposeId = hold_disposeId; + + if(_sync_jobQueue != null) + notifyAll(); // notify waiters (e.g. this is an asyncQueue and there is a sync waiting) + } + else + quit = false; + + } + } + } + + return result; + } + + /** + * If the given disposeId is registered, interrupts the worker thread. + * + * @param disposeId the dispose id. + */ + synchronized void dispose(Object disposeId, Throwable throwable) { + if(_sync_jobQueue == null) { // dispose only sync queues + _doDispose = disposeId; + _throwable = throwable; + + // get thread out of wait and let it throw the throwable + if(DEBUG) System.err.println(getClass().getName() + ".dispose - notifying thread"); + + notifyAll(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/Message.java b/ridljar/com/sun/star/lib/uno/environments/remote/Message.java new file mode 100644 index 000000000..b34295ae3 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/Message.java @@ -0,0 +1,189 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +import com.sun.star.lib.uno.typedesc.MethodDescription; +import com.sun.star.lib.uno.typedesc.TypeDescription; +import com.sun.star.uno.XCurrentContext; + +/** + * A remote request or reply message. + */ +public class Message { + public Message( + ThreadId threadId, boolean request, String objectId, + TypeDescription type, MethodDescription method, boolean synchronous, + XCurrentContext currentContext, boolean abnormalTermination, + Object result, Object[] arguments) + { + this.threadId = threadId; + this.request = request; + this.objectId = objectId; + this.type = type; + this.method = method; + this.synchronous = synchronous; + this.currentContext = currentContext; + this.abnormalTermination = abnormalTermination; + this.result = result; + this.arguments = arguments; + } + + /** + * Returns the thread ID of the message. + * + * <p>Valid for all kinds of messages.</p> + * + * @return the (non-<code>null</code>) thread ID. + */ + public final ThreadId getThreadId() { + return threadId; + } + + /** + * Returns whether the message is a request or a reply. + * + * <p>Valid for all kinds of messages.</p> + * + * @return <code>true</code> for a request, <code>false</code> for a reply. + */ + public final boolean isRequest() { + return request; + } + + /** + * Returns the object ID of a request message. + * + * <p>Valid only for request messages.</p> + * + * @return the (non-<code>null</code>) object ID for a request, + * <code>null</code> for a reply. + */ + public final String getObjectId() { + return objectId; + } + + /** + * Returns the type of a request message. + * + * <p>Valid only for request messages.</p> + * + * @return the (non-<code>null</code>) type for a request, <code>null</code> + * for a reply. + */ + public final TypeDescription getType() { + return type; + } + + /** + * Returns the method description of a request message. + * + * <p>Valid only for request messages. The returned + * <code>MethodDescription</code> is consistent with the type of the + * message.</p> + * + * @return the (non-<code>null</code>) method description for a request, + * <code>null</code> for a reply. + */ + public final MethodDescription getMethod() { + return method; + } + + /** + * Returns whether the request message is synchronous. + * + * <p>Valid only for request messages.</p> + * + * @return <code>true</code> for a synchronous request, <code>false</code> + * for an asynchronous request or a reply. + */ + public final boolean isSynchronous() { + return synchronous; + } + + /** + * Returns the current context of a request message. + * + * <p>Valid only for request messages.</p> + * + * @return the current context (which may be <code>null</code>) for a + * request, <code>null</code> for a reply. + */ + public XCurrentContext getCurrentContext() { + return currentContext; + } + + /** + * Returns whether the reply message represents abnormal termination. + * + * <p>Valid only for reply messages.</p> + * + * @return <code>true</code> for a reply that represents abnormal + * termination, <code>false</code> for a reply that represents normal + * termination or a request. + */ + public final boolean isAbnormalTermination() { + return abnormalTermination; + } + + /** + * Returns the result of a reply message. + * + * <p>Valid only for reply messages.</p> + * + * @return any (possibly <code>null</code>) return value for a reply that + * represents normal termination, the (non-<code>null</code>) exception for + * a reply that represents abnormal termination, <code>null</code> for a + * request. + */ + public final Object getResult() { + return result; + } + + /** + * Returns the arguments of a message. + * + * <p>Valid only for request messages and reply messages that represent + * normal termination. Any returned array must not be modified.</p> + * + * @return the in and in– { + * }out arguments for a request (possibly + * <code>null</code> for a parameterless function), the out and in– { + * }out + * arguments for a reply that represents normal termination (possibly + * <code>null</code> for a parameterless function), <code>null</code> for a + * reply that represents abnormal termination. + */ + public final Object[] getArguments() { + return arguments; + } + + private final ThreadId threadId; + private final boolean request; + private final String objectId; + private final TypeDescription type; + private final MethodDescription method; + private final boolean synchronous; + private final XCurrentContext currentContext; + private final boolean abnormalTermination; + private final Object result; + private final Object[] arguments; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/NativeThreadPool.java b/ridljar/com/sun/star/lib/uno/environments/remote/NativeThreadPool.java new file mode 100644 index 000000000..f77bbb466 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/NativeThreadPool.java @@ -0,0 +1,94 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +final class NativeThreadPool implements IThreadPool { + public NativeThreadPool() { + pool = create(); + } + + public ThreadId getThreadId() { + return new ThreadId(threadId()); + } + + public void attach() { + attach(pool); + } + + public Object attach(ThreadId id) { + attach(); + return null; + } + + public void detach() { + detach(pool); + } + + public void detach(Object handle, ThreadId id) { + detach(); + } + + public Object enter() throws Throwable { + Job job = enter(pool); + if (job == null) { + throw dispose; + } + return job.execute(); + } + + public Object enter(Object handle, ThreadId id) throws Throwable { + return enter(); + } + + public void putJob(Job job) { + putJob( + pool, job.getThreadId().getBytes(), job, job.isRequest(), + job.isRequest() && !job.isSynchronous()); + } + + public void dispose(Throwable throwable) { + dispose = throwable; + dispose(pool); + } + + public void destroy() { + destroy(pool); + } + + // The native implementation is in + // bridges/source/jni_uno/nativethreadpool.cxx: + static { + System.loadLibrary("java_uno"); + } + private static native byte[] threadId(); + private static native long create(); + private static native void attach(long pool); + private static native Job enter(long pool); + private static native void detach(long pool); + private static native void putJob( + long pool, byte[] threadId, Job job, boolean request, boolean oneWay); + private static native void dispose(long pool); + private static native void destroy(long pool); + + private final long pool; + private volatile Throwable dispose; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/ThreadId.java b/ridljar/com/sun/star/lib/uno/environments/remote/ThreadId.java new file mode 100644 index 000000000..f8dacc78c --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/ThreadId.java @@ -0,0 +1,109 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLong; + +import com.sun.star.uno.UnoRuntime; + +public final class ThreadId { + public static ThreadId createFresh() { + long c = count.getAndIncrement(); + try { + return new ThreadId((PREFIX + c).getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("this cannot happen: " + e); + } + } + + public ThreadId(byte[] id) { + this.id = id; + } + + /** + * Indicates whether some other object is equal to this one. + * + * @param obj the reference object with which to compare. + * @return <code>true</code> if this object is the same as the obj argument; + * <code>false</code> otherwise. + * + * @see java.lang.Object#equals + */ + @Override + public boolean equals(Object obj) { + return obj instanceof ThreadId + && Arrays.equals(id, ((ThreadId) obj).id); + } + + /** + * Returns a hash code value for the object. + * + * @return a hash code value for this object. + * @see java.lang.Object#hashCode + */ + @Override + public int hashCode() { + int h = hash; + if (h == 0) { + // Same algorithm as java.util.List.hashCode (also see Java 1.5 + // java.util.Arrays.hashCode(byte[])): + h = 1; + for (int i = 0; i < id.length; ++i) { + h = 31 * h + id[i]; + } + hash = h; + } + return h; + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + * @see java.lang.Object#toString + */ + @Override + public String toString() { + StringBuffer b = new StringBuffer("[ThreadId:"); + for (int i = 0; i < id.length; ++i) { + String n = Integer.toHexString(id[i] & 0xFF); + if (n.length() == 1) { + b.append('0'); + } + b.append(n); + } + b.append(']'); + return b.toString(); + } + + public byte[] getBytes() { + return id; + } + + private static final String PREFIX = "java:" + UnoRuntime.getUniqueKey() + ":"; + private static final AtomicLong count = new AtomicLong(0); + + private final byte[] id; + private int hash = 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/ThreadPoolManager.java b/ridljar/com/sun/star/lib/uno/environments/remote/ThreadPoolManager.java new file mode 100644 index 000000000..2014f5167 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/ThreadPoolManager.java @@ -0,0 +1,74 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +/** + * Manages the UNO thread pool factory. + * + * <P>The thread pool factory is a process-wide resource. It is important that + * all UNO environments within a process share the same thread pool mechanisms: + * if a synchronous UNO call is bridged out from one local UNO environment over + * one remote bridge, and recursively calls back into another local UNO + * environment over another remote bridge, the code in the second environment + * should be executed in the thread that did the original call from the first + * environment.</P> + * + * <P>There are both a Java and a native thread pool factory. A pure Java + * process will always use the Java thread pool factory. A mixed process uses + * the system property <CODE>org.openoffice.native</CODE> (to be set by the + * native code that starts the JVM) to determine which implementation + * to use.</P> + */ +public final class ThreadPoolManager { + /** + * Creates a thread pool instance. + * + * @return a new thread pool instance; will never be <CODE>null</CODE>. + */ + public static synchronized IThreadPool create() { + if (useNative) { + return new NativeThreadPool(); + } else { + if (javaFactory == null) { + javaFactory = new JavaThreadPoolFactory(); + } + return javaFactory.createThreadPool(); + } + } + + /** + * Leads to using the native thread pool factory, unless a Java thread pool + * has already been created. + * + * @return <CODE>false</CODE> if a Java thread pool has already been created. + */ + public static synchronized boolean useNative() { + useNative = javaFactory == null; + return useNative; + } + + private static boolean useNative + = System.getProperty("org.openoffice.native") != null; + private static JavaThreadPoolFactory javaFactory = null; + + private ThreadPoolManager() {} // do not instantiate +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/environments/remote/remote_environment.java b/ridljar/com/sun/star/lib/uno/environments/remote/remote_environment.java new file mode 100644 index 000000000..09259c8cb --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/environments/remote/remote_environment.java @@ -0,0 +1,66 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.environments.remote; + +import com.sun.star.uno.IEnvironment; +import com.sun.star.uno.Type; + +public final class remote_environment implements IEnvironment { + public remote_environment(Object context) { + this.context = context; + } + + public Object getContext() { + return context; + } + + public String getName() { + return "remote"; + } + + public Object registerInterface(Object object, String[] oid, Type type) { + throw new UnsupportedOperationException( + "java_remote environment is not functional"); + } + + public void revokeInterface(String oid, Type type) { + throw new UnsupportedOperationException( + "java_remote environment is not functional"); + } + + public Object getRegisteredInterface(String oid, Type type) { + throw new UnsupportedOperationException( + "java_remote environment is not functional"); + } + + public String getRegisteredObjectIdentifier(Object object) { + throw new UnsupportedOperationException( + "java_remote environment is not functional"); + } + + public void list() { + throw new UnsupportedOperationException( + "java_remote environment is not functional"); + } + + private final Object context; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/helper/ComponentBase.java b/ridljar/com/sun/star/lib/uno/helper/ComponentBase.java new file mode 100644 index 000000000..d886ef702 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/ComponentBase.java @@ -0,0 +1,136 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.helper; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; +import com.sun.star.lang.EventObject; +import com.sun.star.uno.Type; + +/** This class can be used as the base class for UNO components. In addition to the functionality ,which + * is inherited from WeakBase, it implements com.sun.star.lang.XComponent. + */ +public class ComponentBase extends WeakBase implements XComponent +{ + private static final boolean DEBUG= false; + protected MultiTypeInterfaceContainer listenerContainer; + protected boolean bInDispose= false; + protected boolean bDisposed= false; + static final Type EVT_LISTENER_TYPE= new Type(XEventListener.class); + + + /** Creates a new instance of CompBase */ + public ComponentBase() + { + super(); + listenerContainer= new MultiTypeInterfaceContainer(); + } + + /** Override to perform extra clean-up work. Provided for subclasses. It is + called during dispose() + */ + protected void preDisposing() + { + } + /** Override to become notified right before the disposing action is performed. + */ + protected void postDisposing() + { + } + + + /** Method of XComponent. It is called by the owning client when the component is not needed + * anymore. The registered listeners are notified that this method has been called. + */ + public void dispose() + { + // Determine in a thread-safe way if this is the first call to this method. + // Only then we proceed with the notification of event listeners. + // It is an error to call this method more than once. + boolean bDoDispose= false; + synchronized (this) + { + if ( ! bInDispose && ! bDisposed) + { + bDoDispose= true; + bInDispose= true; + } + } + // The notification occurs in an unsynchronized block in order to avoid + // deadlocks if one of the listeners calls back in a different thread on + // a synchronized method which uses the same object. + if (bDoDispose) + { + try + { + preDisposing(); + listenerContainer.disposeAndClear(new EventObject(this)); + //notify subclasses that disposing is in progress + postDisposing(); + } + finally + { + // finally makes sure that the flags are set even if a RuntimeException is thrown. + // That ensures that this function is only called once. + synchronized (this) + { + bDisposed= true; + bInDispose= false; + } + } + } + else + { + // in a multithreaded environment, it can't be avoided, that dispose is called twice. + // However this condition is traced, because it MAY indicate an error. + if (DEBUG) + System.out.println("OComponentHelper::dispose() - dispose called twice" ); + } + } + + /** Method of XComponent. + */ + public void removeEventListener(XEventListener xEventListener) + { + listenerContainer.removeInterface( EVT_LISTENER_TYPE, xEventListener); + } + + public void addEventListener(XEventListener listener) + { + boolean bDoDispose= false; + synchronized (this) + { + if (bDisposed || bInDispose) + bDoDispose= true; + else + listenerContainer.addInterface(EVT_LISTENER_TYPE, listener); + } + if (bDoDispose ) + { + listener.disposing( new EventObject(this)); + } + } + + @Override + protected void finalize() throws Throwable + { + if ( ! bInDispose && ! bDisposed) + dispose(); + super.finalize(); + } +} diff --git a/ridljar/com/sun/star/lib/uno/helper/Factory.java b/ridljar/com/sun/star/lib/uno/helper/Factory.java new file mode 100644 index 000000000..056d9549d --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/Factory.java @@ -0,0 +1,291 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.helper; + +import com.sun.star.uno.XComponentContext; +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XInitialization; +import com.sun.star.registry.XRegistryKey; +import com.sun.star.uno.UnoRuntime; + + +/** Factory helper class supporting com.sun.star.lang.XServiceInfo and + com.sun.star.lang.XSingleComponentFactory. + + <p> + Note: This factory implementation does not support lang.XSingleServiceFactory. + </p> +*/ +public class Factory + extends ComponentBase + implements XSingleComponentFactory, XServiceInfo +{ + private static final boolean DEBUG = false; + + /** Creates an object factory supporting interfaces + com.sun.star.lang.XSingleComponentFactory and + com.sun.star.lang.XServiceInfo + + @param impl_class + implementation class + @param impl_name + implementation name + @param supported_services + services implemented + @return + object factory + + @since UDK 3.2.13 + */ + public static XSingleComponentFactory createComponentFactory( + Class impl_class, String impl_name, String supported_services [] ) + throws com.sun.star.uno.RuntimeException + { + return new Factory( impl_class, impl_name, supported_services ); + } + + /** Creates an object factory supporting interfaces + com.sun.star.lang.XSingleComponentFactory and + com.sun.star.lang.XServiceInfo + + The implementation name is the name of the implementation class. + + @param impl_class + implementation class + @param supported_services + services implemented + @return + object factory + */ + public static XSingleComponentFactory createComponentFactory( + Class impl_class, String supported_services [] ) + throws com.sun.star.uno.RuntimeException + { + return createComponentFactory( + impl_class, impl_class.getName(), supported_services ); + } + /** Writes component's implementation info to given registry key. + + @param impl_name + name of implementation + @param supported_services + supported services of implementation + @param xKey + registry key to write to + @return + success + */ + public static boolean writeRegistryServiceInfo( + String impl_name, String supported_services [], XRegistryKey xKey ) + { + try + { + XRegistryKey xNewKey = xKey.createKey( "/" + impl_name + "/UNO/SERVICES" ); + for ( int nPos = 0; nPos < supported_services.length; ++nPos ) + { + xNewKey.createKey( supported_services[ nPos ] ); + } + return true; + } + catch (com.sun.star.registry.InvalidRegistryException exc) + { + if (DEBUG) + { + System.err.println( + "##### " + Factory.class.getName() + ".writeRegistryServiceInfo -- exc: " + + exc.toString() ); + } + } + return false; + } + + + private final String m_impl_name; + private final String [] m_supported_services; + private final Class<?> m_impl_class; + private final java.lang.reflect.Method m_method; + private final java.lang.reflect.Constructor m_ctor; + + private Factory( + Class impl_class, String impl_name, String supported_services [] ) + throws com.sun.star.uno.DeploymentException + { + m_impl_name = impl_name; + m_supported_services = supported_services; + m_impl_class = impl_class; + + Class params [] = new Class [] { XComponentContext.class }; + + if (!java.lang.reflect.Modifier.isPublic( impl_class.getModifiers() )) + { + throw new com.sun.star.uno.DeploymentException("class " + impl_class + " is not public"); + } + + java.lang.reflect.Method tmpMethod = null; + try + { + // seeking for "public static Object __create( XComponentContext )" + tmpMethod = m_impl_class.getMethod( "__create", params ); + int mod = tmpMethod.getModifiers(); + if (!tmpMethod.getReturnType().equals( Object.class ) || + !java.lang.reflect.Modifier.isStatic( mod ) || + !java.lang.reflect.Modifier.isPublic( mod )) + { + tmpMethod = null; + } + } + catch (Exception exc) + { + } + m_method = tmpMethod; + + + java.lang.reflect.Constructor tmpCtor = null; + if (null == m_method) + { + try + { + // ctor with context + tmpCtor = m_impl_class.getConstructor( params ); + } + catch (Exception exc) + { + } + if (tmpCtor != null) + { + if (!java.lang.reflect.Modifier.isPublic( tmpCtor.getModifiers() )) + { + throw new com.sun.star.uno.DeploymentException("constructor with XComponentContext param for class " + impl_class + " is not public"); + } + } + else + { + // else take default ctor + java.lang.reflect.Constructor defaultCtor; + try + { + defaultCtor = m_impl_class.getConstructor(new Class[0]); + } + catch (Exception exc2) + { + throw new com.sun.star.uno.DeploymentException(exc2, "class " + impl_class + " has no means of creating it, cannot find a __create method or a useful constructor."); + } + if (!java.lang.reflect.Modifier.isPublic( defaultCtor.getModifiers() )) + { + throw new com.sun.star.uno.DeploymentException("default constructor for class " + impl_class + " is not public"); + } + } + } + m_ctor = tmpCtor; + } + + + private Object instantiate( XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + try + { + if (DEBUG) + System.out.print( "instantiating " + m_impl_class.toString() + " using " ); + if (null != m_method) + { + if (DEBUG) + System.out.println( "__create( XComponentContext )..." ); + return m_method.invoke( null, new Object [] { xContext } ); + } + if (null != m_ctor) + { + if (DEBUG) + System.out.println( "ctor( XComponentContext )..." ); + return m_ctor.newInstance( new Object [] { xContext } ); + } + if (DEBUG) + System.out.println( "default ctor..." ); + return m_impl_class.newInstance(); // default ctor + } + catch (java.lang.reflect.InvocationTargetException exc) + { + Throwable targetException = exc.getTargetException(); + if (targetException instanceof java.lang.RuntimeException) + throw (java.lang.RuntimeException)targetException; + else if (targetException instanceof com.sun.star.uno.RuntimeException) + throw (com.sun.star.uno.RuntimeException)targetException; + else if (targetException instanceof com.sun.star.uno.Exception) + throw (com.sun.star.uno.Exception)targetException; + else + throw new com.sun.star.uno.Exception(targetException, targetException.getMessage(), this); + } + catch (IllegalAccessException exc) + { + throw new com.sun.star.uno.RuntimeException( exc, exc.getMessage(), this); + } + catch (InstantiationException exc) + { + throw new com.sun.star.uno.RuntimeException( exc, exc.getMessage(), this); + } + } + // XSingleComponentFactory impl + + public final Object createInstanceWithContext( + XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + return instantiate( xContext ); + } + + public final Object createInstanceWithArgumentsAndContext( + Object arguments [], XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + Object inst = instantiate( xContext ); + XInitialization xInit = UnoRuntime.queryInterface( + XInitialization.class, inst ); + if (null == xInit) + { + throw new com.sun.star.lang.IllegalArgumentException( + "cannot pass arguments to component; no XInitialization implemented!", this, + (short)0 ); + } + xInit.initialize( arguments ); + return inst; + } + + // XServiceInfo impl + + public final String getImplementationName() + { + return m_impl_name; + } + + public final boolean supportsService( String service_name ) + { + for ( int nPos = 0; nPos < m_supported_services.length; ++nPos ) + { + if (m_supported_services[ nPos ].equals( service_name )) + return true; + } + return false; + } + + public final String [] getSupportedServiceNames() + { + return m_supported_services; + } +} + diff --git a/ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java b/ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java new file mode 100644 index 000000000..e858ced81 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java @@ -0,0 +1,864 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.helper; + +import java.util.Iterator; +import java.util.ListIterator; +import java.util.Collection; + +import com.sun.star.lang.EventObject; +import com.sun.star.lang.XEventListener; +import com.sun.star.uno.UnoRuntime; + +/** + * This class is a container for interfaces. + * + * It is intended to be used as storage for UNO interface of a specific type. + * The client has to ensure that the container contains only elements of the same + * type. If one needs to store different types, then one uses OMultiTypeInterfaceContainer. + * When the client calls disposeAndClear, the contained objects are queried for + * com.sun.star.lang.XEventListener and disposing is called. Afterwards + * the list cannot be used anymore. + * + * This list does not allow null values. + * All methods are thread-safe. The same holds true for + * iterators, issued by this class. Several iterators can exist at the same time and can also + * be modified (java.util.ListIterator.add, java.util.ListIterator.remove etc.). To make this work, + * the InterfaceContainer provides the iterators with copies of the list's data. + * The add and remove calls on the iterator modify the data in the iterator's list as well as + * in InterfaceContainer. Modification on InterfaceContainer, however, are not + * synchronized with existing iterators. For example + * <pre> + * InterfaceContainer cont= new InterfaceContainer(); + * ListIterator it= cont.listIterator(); + * + * cont.add( someInterface); + * // one cannot obtain someInterface through iterator it, + * // instead get a new iterator + * it= cont.listIterator(); + * // it now keeps a fresh copy of cont and hence contains someInterface + * + * // Adding an interface on the iterator will cause the interface also to be added + * // to InterfaceContainer + * it.add( someOtherInterface); + * // someOtherInterface is now in it and cont + * ListIterator it2= cont.listIterator(); + * //someOtherInterface can also be obtained by all newly created iterators, e.g. it2. + * </pre> + * + * The add and remove methods of an iterator work on a particular location within a list, + * dependent on what the value of the iterator's cursor is. After the call the value at the + * appropriate position has been modified. Since the iterator received a copy of InterfaceContainer's + * data, InterfaceContainer may have been modified (by List methods or through other iterators). + * Therefore both data sets may not contain the same elements anymore. Consequently, a List method + * that modifies data, does not modify InterfaceContainer's data at a certain index + * (according to the iterators cursor). Instead, new elements are added at the end of list. When + * Iterator.remove is called, then the first occurrence of that element in InterfaceContainer + * is removed. + * ListIterator.set is not supported. + * + * A lot of methods resemble those of the to java.util.List interface, although + * this class does not implement it. However, the list iterators returned, for example by + * the listIterator method implement the java.util.ListIterator interface. + * Implementing the List interface would mean to support all none - optional methods as + * prescribed by the interface declaration. Among those is the subList method which returns + * a range of values of the list's data wrapped in a List implementation. Changes to the sub + * list have to cause changes in the main list. This is a problem, since this class is to be + * used in a multi-threaded environment. The sub list could work on a copy as the iterators + * do, but all the functions which work on a given index could not be properly supported. + * Unfortunately, the List interface documentation states that all optional methods implemented + * by the list have to be implemented in the sub list. That would mean to do without all those + * critical methods, although they might work well in the "main list" (as opposed to sub list). + */ +public class InterfaceContainer implements Cloneable +{ + static final boolean DEBUG= false; + /** + * The array buffer into which the elements of the ArrayList are stored. + * The capacity of the ArrayList is the length of this array buffer. + */ + Object elementData[]; + + /** + * The size of the ArrayList (the number of elements it contains). + */ + private int size; + + + /** Creates a new instance of InterfaceContainer */ + public InterfaceContainer() + { + this(10); + } + /** + * Constructs an empty list with the specified initial capacity. + * + * @param initialCapacity the initial capacity of the list. + * @exception IllegalArgumentException if the specified initial capacity + * is negative + */ + public InterfaceContainer(int initialCapacity) + { + if (initialCapacity < 0) + throw new java.lang.IllegalArgumentException("Illegal Capacity: "+ + initialCapacity); + this.elementData = new Object[initialCapacity]; + } + + private InterfaceContainer(Object[] data) { + elementData = data; + size = elementData == null ? 0 : elementData.length; + } + + /** + * Trims the capacity of this <code>ArrayList</code> instance to be the + * list's current size. An application can use this operation to minimize + * the storage of an <code>ArrayList</code> instance. + */ + synchronized public void trimToSize() + { + int oldCapacity = elementData.length; + if (size < oldCapacity) + { + Object oldData[] = elementData; + elementData = new Object[size]; + System.arraycopy(oldData, 0, elementData, 0, size); + } + } + + /** + * Increases the capacity of this <code>ArrayList</code> instance, if + * necessary, to ensure that it can hold at least the number of elements + * specified by the minimum capacity argument. + * + * @param minCapacity the desired minimum capacity. + */ + synchronized public void ensureCapacity(int minCapacity) + { + int oldCapacity = elementData.length; + if (minCapacity > oldCapacity) + { + Object oldData[] = elementData; + int newCapacity = (oldCapacity * 3)/2 + 1; + if (newCapacity < minCapacity) + newCapacity = minCapacity; + elementData = new Object[newCapacity]; + System.arraycopy(oldData, 0, elementData, 0, size); + } + } + + /** + * Appends the specified element to the end of this list. + * + * @param o element to be appended to this list. + * @return <code>true</code> (as per the general contract of Collection.add). + */ + synchronized public boolean add(Object o) + { + boolean ret= false; + if (elementData != null && o != null) + { + ensureCapacity(size + 1); // Increments modCount!! + elementData[size++] = o; + ret= true; + } + return ret; + } + + /** + * Inserts the specified element at the specified position in this + * list. Shifts the element currently at that position (if any) and + * any subsequent elements to the right (adds one to their indices). + * + * @param index index at which the specified element is to be inserted. + * @param element element to be inserted. + * @throws IndexOutOfBoundsException if index is out of range + * <code>(index < 0 || index > size())</code>. + */ + synchronized public void add(int index, Object element) + { + if (elementData != null && element != null) + { + if (index > size || index < 0) + throw new IndexOutOfBoundsException( + "Index: "+index+", Size: "+size); + + ensureCapacity(size+1); + System.arraycopy(elementData, index, elementData, index + 1, + size - index); + elementData[index] = element; + size++; + } + } + + + /** + * Appends all of the elements in the specified Collection to the end of + * this list, in the order that they are returned by the + * specified Collection's Iterator. The behavior of this operation is + * undefined if the specified Collection is modified while the operation + * is in progress. (This implies that the behavior of this call is + * undefined if the specified Collection is this list, and this + * list is nonempty.) + * + * @param c the elements to be inserted into this list. + * @throws IndexOutOfBoundsException if index out of range <code>(index + * < 0 || index > size())</code>. + * @return true if an element was inserted. + */ + synchronized public boolean addAll(Collection c) + { + int numNew = c.size(); + ensureCapacity(size + numNew); + + Iterator e = c.iterator(); + for (int i=0; i<numNew; i++) + { + Object o= e.next(); + if (o != null) + elementData[size++] = o; + } + return numNew != 0; + } + /** + * Inserts all of the elements in the specified Collection into this + * list, starting at the specified position. Shifts the element + * currently at that position (if any) and any subsequent elements to + * the right (increases their indices). The new elements will appear + * in the list in the order that they are returned by the + * specified Collection's iterator. + * + * @param index index at which to insert first element + * from the specified collection. + * @param c elements to be inserted into this list. + * @throws IndexOutOfBoundsException if index out of range <code>(index + * < 0 || index > size())</code>. + * @return true if an element was inserted. + */ + synchronized public boolean addAll(int index, Collection c) + { + boolean ret= false; + if (elementData != null) + { + if (index > size || index < 0) + throw new IndexOutOfBoundsException( + "Index: "+index+", Size: "+size); + // only add the non-null elements + int sizeCol= c.size(); + Object[] arColl= new Object[sizeCol]; + Iterator icol= c.iterator(); + int curIndex= 0; + for (int i=0; i < sizeCol; i++) + { + Object o= icol.next(); + if (o != null) + arColl[curIndex++]= o; + } + int numNew = curIndex; + ensureCapacity(size + numNew); // Increments modCount!! + + int numMoved = size - index; + if (numMoved > 0) + System.arraycopy(elementData, index, elementData, index + numNew, + numMoved); + + for (int i=0; i<numNew; i++) + { + elementData[index++]= arColl[i]; + } + size += numNew; + ret= numNew != 0; + } + return ret; + } + + /** + * Removes all of the elements from this list. The list will + * be empty after this call returns. + */ + synchronized public void clear() + { + if (elementData != null) + { + // Let gc do its work + for (int i = 0; i < size; i++) + elementData[i] = null; + + size = 0; + } + } + /** + * Returns <code>true</code> if this list contains the specified element. + * + * @param elem element whose presence in this List is to be tested. + * @return <code>true</code> if this list contains the specified element. + */ + synchronized public boolean contains(Object elem) + { + return indexOf(elem) >= 0; + } + + synchronized public boolean containsAll(Collection collection) + { + boolean retVal= true; + if (elementData != null && collection != null) + { + Iterator it= collection.iterator(); + while (it.hasNext()) + { + Object obj= it.next(); + if (!contains(obj)) + { + retVal= false; + break; + } + } + } + return retVal; + } + /** + * Returns the element at the specified position in this list. + * + * @param index index of element to return. + * @return the element at the specified position in this list. + * @throws IndexOutOfBoundsException if index is out of range <code>(index + * < 0 || index >= size())</code>. + */ + synchronized public Object get(int index) + { + if (elementData != null) + { + RangeCheck(index); + return elementData[index]; + } + return null; + } + + /** + * Searches for the first occurrence of the given argument, testing + * for equality using the <code>equals</code> method. + * + * @param elem an object. + * @return the index of the first occurrence of the argument in this + * list; returns <code>-1</code> if the object is not found. + * @see Object#equals(Object) + */ + synchronized public int indexOf(Object elem) + { + if (elementData == null || elem == null) { + return -1; + } + + int index= -1; + + for (int i = 0; i < size; i++) + { + if (elem == elementData[i]) + { + index= i; + break; + } + } + + if (index == -1) + { + for (int i = 0; i < size; i++) + { + if (UnoRuntime.areSame(elem, elementData[i])) + { + index= i; + break; + } + } + } + return index; + } + /** + * Tests if this list has no elements. + * + * @return <code>true</code> if this list has no elements; + * <code>false</code> otherwise. + */ + synchronized public boolean isEmpty() + { + return size == 0; + } + + synchronized public Iterator iterator() + { + if (elementData != null) + { + InterfaceContainer aCopy= (InterfaceContainer) clone(); + return new Itr(aCopy); + } + return null; + } + /** + * Returns the index of the last occurrence of the specified object in + * this list. + * + * @param elem the desired element. + * @return the index of the last occurrence of the specified object in + * this list; returns -1 if the object is not found. + */ + synchronized public int lastIndexOf(Object elem) + { + if (elementData == null || elem == null) { + return -1; + } + + int index= -1; + + for (int i = size-1; i >= 0; i--) + { + if (elem == elementData[i]) + { + index= i; + break; + } + } + if (index == -1) + { + for (int i = size-1; i >= 0; i--) + { + if (UnoRuntime.areSame(elem, elementData[i])) + { + index= i; + break; + } + } + } + return index; + } + + /** + * Returns a shallow copy of this <code>ArrayList</code> instance. The contained + * references are copied but the objects not. + * + * @return a clone of this <code>List</code> instance. + */ + @Override + synchronized public Object clone() + { + Object[] data; + if (elementData == null) { + data = null; + } else { + data = new Object[size]; + System.arraycopy(elementData, 0, data, 0, size); + } + return new InterfaceContainer(data); + } + synchronized public ListIterator listIterator() + { + return listIterator(0); + } + + /** The iterator keeps a copy of the list. Changes to InterfaceContainer do not + * affect the data of the iterator. Conversely, changes to the iterator are effect + * InterfaceContainer. + * @param index the starting offset into the list. + * @return a new iterator. + */ + synchronized public ListIterator listIterator(int index) + { + if (elementData != null) + { + InterfaceContainer aCopy= (InterfaceContainer) clone(); + return new LstItr(aCopy, index); + } + return null; + } + /** + * Removes the element at the specified position in this list. + * Shifts any subsequent elements to the left (subtracts one from their + * indices). + * + * @param index the index of the element to removed. + * @return the element that was removed from the list. + * @throws IndexOutOfBoundsException if index out of range <code>(index + * < 0 || index >= size())</code>. + */ + synchronized public Object remove(int index) + { + Object ret= null; + if (elementData != null) + { + RangeCheck(index); + ret= elementData[index]; + + int numMoved = size - index - 1; + if (numMoved > 0) + System.arraycopy(elementData, index+1, elementData, index, + numMoved); + elementData[--size] = null; // Let gc do its work + } + return ret; + } + + + /** Parameter obj may... or may not. What did the original author want + * to tell us here? + * @param obj the object to be removed. + * @return true if obj was successfully removed from the list. + */ + synchronized public boolean remove(Object obj) + { + boolean ret= false; + if (elementData != null && obj != null) + { + int index= indexOf(obj); + if (index != -1) + { + ret= true; + remove(index); + } + } + return ret; + } + + synchronized public boolean removeAll(Collection collection) + { + boolean retVal= false; + if (elementData != null && collection != null) + { + Iterator it= collection.iterator(); + while (it.hasNext()) + { + Object obj= it.next(); + boolean bMod= remove( obj); + if (bMod) + retVal= true; + } + } + return retVal; + } + + synchronized public boolean retainAll(Collection collection) + { + if (elementData == null || collection == null) { + return false; + } + + boolean retVal= false; + // iterate over data + Object[] arRetained= new Object[size]; + int indexRetained= 0; + for(int i= 0; i < size; i++) + { + Object curElem= elementData[i]; + // try to find the element in collection + Iterator itColl= collection.iterator(); + boolean bExists= false; + while (itColl.hasNext()) + { + if (curElem == itColl.next()) + { + // current element is in collection + bExists= true; + break; + } + } + if (!bExists) + { + itColl= collection.iterator(); + while (itColl.hasNext()) + { + Object o= itColl.next(); + if (o != null && UnoRuntime.areSame(o, curElem)) + { + bExists= true; + break; + } + } + } + if (bExists) + arRetained[indexRetained++]= curElem; + } + retVal= size != indexRetained; + if (indexRetained > 0) + { + elementData= arRetained; + size= indexRetained; + } + return retVal; + } + + + /** Not supported. + * @param index index of element to replace. + * @param element element to be stored at the specified position. + * @return the element previously at the specified position. + * @throws IndexOutOfBoundsException if index out of range + * <code>(index < 0 || index >= size())</code>. + */ + synchronized public Object set(int index, Object element) + { + Object ret= null; + if (elementData != null && element != null) + { + RangeCheck(index); + ret = elementData[index]; + elementData[index] = element; + } + return ret; + } + + /** + * Returns the number of elements in this list. + * + * @return the number of elements in this list. + */ + synchronized public int size() + { + if (elementData != null) + return size; + return 0; + } + + + /** + * Returns an array containing all of the elements in this list + * in the correct order. + * + * @return an array containing all of the elements in this list + * in the correct order. + */ + synchronized public Object[] toArray() + { + if (elementData != null) + { + Object[] result = new Object[size]; + System.arraycopy(elementData, 0, result, 0, size); + return result; + } + return null; + } + + /** + * Returns an array containing all of the elements in this list in the + * correct order. The runtime type of the returned array is that of the + * specified array. If the list fits in the specified array, it is + * returned therein. Otherwise, a new array is allocated with the runtime + * type of the specified array and the size of this list.<p> + * + * If the list fits in the specified array with room to spare (i.e., the + * array has more elements than the list), the element in the array + * immediately following the end of the collection is set to + * <code>null</code>. This is useful in determining the length of the list + * <i>only</i> if the caller knows that the list does not contain any + * <code>null</code> elements. + * + * @param a the array into which the elements of the list are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose. + * @return an array containing the elements of the list. + * @throws ArrayStoreException if the runtime type of a is not a supertype + * of the runtime type of every element in this list. + */ + synchronized public Object[] toArray(Object a[]) + { + if (a.length < size) + a = (Object[])java.lang.reflect.Array.newInstance( + a.getClass().getComponentType(), size); + if (elementData != null) + System.arraycopy(elementData, 0, a, 0, size); + + if (a.length > size) + a[size] = null; + + return a; + } + + /** + * Check if the given index is in range. If not, throw an appropriate + * runtime exception. + */ + private void RangeCheck(int index) + { + if (index >= size || index < 0) + throw new IndexOutOfBoundsException( + "Index: "+index+", Size: "+size); + } + + public void disposeAndClear(EventObject evt) + { + Iterator aIt; + synchronized (this) + { + aIt= iterator(); + // Release containers if new entries occur in disposing; + // set the member to null, the iterator delete the values + clear(); + elementData= null; + size= 0; + } + if (aIt != null) + { + while( aIt.hasNext() ) + { + try + { + Object o= aIt.next(); + XEventListener evtListener= UnoRuntime.queryInterface( + XEventListener.class, o); + if( evtListener != null ) + evtListener.disposing( evt ); + } + catch ( RuntimeException e) + { + // be robust, if e.g. a remote bridge has disposed already. + // there is no way, to delegate the error to the caller :o(. + } + } + } + } + + + private class Itr implements Iterator + { + InterfaceContainer dataIt; + /** + * Index of element to be returned by subsequent call to next. + */ + int cursor= 0; + /** + * Index of element returned by most recent call to next or + * previous. Reset to -1 if this element is deleted by a call + * to remove. + */ + int lastRet = -1; + + /** The object that has been returned by most recent call to next + * or previous. Reset to null if this element is deleted by a call + * to remove. + */ + Object lastRetObj= null; + + Itr(InterfaceContainer _data) + { + dataIt= _data; + } + + synchronized public boolean hasNext() + { + return cursor !=dataIt.size(); + } + + public synchronized Object next() + { + try + { + Object next = dataIt.get(cursor); + lastRet = cursor++; + lastRetObj= next; + return next; + } + catch(java.lang.IndexOutOfBoundsException e) + { + java.util.NoSuchElementException ex2 = new java.util.NoSuchElementException(); + ex2.initCause(e); + throw ex2; + } + } + + /** Removes the interface from the list, that has been last returned by a + * call to next(). This is done according to the specification of the interface + * method. The element is also removed from InterfaceContainer but independent + * of the location. If the element is multiple times in InterfaceContainer then + * it is up to the java.util.ArrayList implementation what element is removed. + */ + public synchronized void remove() + { + if (lastRet == -1) + throw new IllegalStateException(); + // Remove the entry from InterfaceContainer. + InterfaceContainer.this.remove(lastRetObj); + dataIt.remove(lastRet); + + if (lastRet < cursor) + cursor--; + lastRet = -1; + lastRetObj= null; + } + } + + private class LstItr extends Itr implements ListIterator + { + + LstItr(InterfaceContainer _data, int _index) + { + super(_data); + cursor= _index; + } + + /** Inserts an element to the iterators list according to the specification + * of this interface method. The element is also added to InterfaceContainer + * but its location within the list cannot be guaranteed. + */ + public synchronized void add(Object o) + { + InterfaceContainer.this.add(o); + dataIt.add(cursor++, o); + lastRet = -1; + lastRetObj= null; + } + + synchronized public boolean hasPrevious() + { + return cursor != 0; + } + + synchronized public int nextIndex() + { + return cursor; + } + + public synchronized Object previous() + { + try + { + Object previous = dataIt.get(--cursor); + lastRet = cursor; + lastRetObj= previous; + return previous; + } catch(IndexOutOfBoundsException e) + { + java.util.NoSuchElementException ex2 = new java.util.NoSuchElementException(); + ex2.initCause(e); + throw ex2; + } + } + + synchronized public int previousIndex() + { + return cursor-1; + } + + /** This is not possible since several iterators can modify InterfaceContainer + */ + public synchronized void set(Object o) + { + throw new UnsupportedOperationException(); + } + + + } // class LstItr +} + diff --git a/ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java b/ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java new file mode 100644 index 000000000..9b061f81c --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java @@ -0,0 +1,155 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.helper; +import com.sun.star.uno.Type; +import com.sun.star.lang.EventObject; +import java.util.HashMap; +import java.util.Map; +import java.util.Iterator; + +public class MultiTypeInterfaceContainer +{ + + private final Map<Object,InterfaceContainer> map= new HashMap<Object,InterfaceContainer>(); + + /** only returns types which have at least one value in InterfaceContainer + * return value can contain an element null, if someone called + * addInterface (null, interf) + * @return an array of types in this container. + */ + synchronized public Type[] getContainedTypes() + { + int size; + Type[] retVal= null; + + if ( (size=map.size()) > 0) + { + Type [] arTypes= new Type[size]; + + int countTypes= 0; + for (Map.Entry<Object,InterfaceContainer> entry : map.entrySet()) + { + InterfaceContainer cont= entry.getValue(); + if (cont != null && cont.size() > 0) + { + Object key = entry.getKey(); + if (key == null) + arTypes[countTypes++]= new Type(); + else if (key instanceof Type) + arTypes[countTypes++]= (Type) key; + else if (key instanceof Class) + arTypes[countTypes++]= new Type((Class) key); + else + arTypes[countTypes++]= new Type(key.getClass()); + } + } + + if (countTypes != size) + { + retVal= new Type[countTypes]; + System.arraycopy(arTypes, 0, retVal, 0, countTypes); + } + else + retVal= arTypes; + } + if (retVal == null) + retVal= new Type[0]; + return retVal; + } + + /** param key can be null + * @param key the object for which the container should be retrieved. + * @return the container that contains the object key, if any. + */ + synchronized public InterfaceContainer getContainer(Object key) + { + InterfaceContainer retVal= null; + for (Map.Entry<Object,InterfaceContainer> entry : map.entrySet()) + { + Object obj= entry.getKey(); + if (obj == null && key == null) + { + retVal= map.get(null); + break; + } + else if( obj != null && obj.equals(key)) + { + retVal= entry.getValue(); + break; + } + } + return retVal; + } + + + synchronized public int addInterface(Object ckey, Object iface) + { + //If the key is a Type then it does not matter if the objects are different + // if they represent the same type. This is because Types overrides hashCode and + // equals. For example: + // Type a= new Type(XInterface.class); + // Type b= new Type(XInterface.class); + // Although a != b , the map interprets both as being the same. + InterfaceContainer cont= map.get(ckey); + if (cont != null) + { + cont.add(iface); + } + else + { + cont= new InterfaceContainer(); + cont.add(iface); + map.put(ckey, cont); + } + return cont.size(); + } + + + synchronized public int removeInterface(Object key, Object iface) + { + int retVal= 0; + InterfaceContainer cont= map.get(key); + if (cont != null) + { + cont.remove(iface); + retVal= cont.size(); + } + return retVal; + } + + public void disposeAndClear(EventObject evt) + { + Iterator<InterfaceContainer> it= null; + synchronized(this) + { + it= map.values().iterator(); + } + while (it.hasNext() ) { + it.next().disposeAndClear(evt); + } + } + + synchronized public void clear() + { + Iterator<InterfaceContainer> it= map.values().iterator(); + while (it.hasNext()) { + it.next().clear(); + } + } +} diff --git a/ridljar/com/sun/star/lib/uno/helper/PropertySet.java b/ridljar/com/sun/star/lib/uno/helper/PropertySet.java new file mode 100644 index 000000000..67a4f0c98 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/PropertySet.java @@ -0,0 +1,1103 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.helper; + +import com.sun.star.uno.Type; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.uno.TypeClass; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.XInterface; +import com.sun.star.uno.Any; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.beans.XPropertyChangeListener; +import com.sun.star.beans.XVetoableChangeListener; +import com.sun.star.beans.PropertyChangeEvent; +import com.sun.star.beans.XPropertySet; +import com.sun.star.beans.Property; +import com.sun.star.beans.PropertyAttribute; +import com.sun.star.beans.UnknownPropertyException; +import com.sun.star.beans.XPropertiesChangeListener; +import com.sun.star.beans.XPropertySetInfo; +import com.sun.star.beans.XFastPropertySet; +import com.sun.star.beans.PropertyVetoException; +import com.sun.star.beans.XMultiPropertySet; +import java.util.Iterator; +import java.util.Collection; +import java.util.HashMap; +import java.lang.reflect.Field; +import com.sun.star.lang.DisposedException; + + +/** This class is an implementation of the interfaces com.sun.star.beans.XPropertySet, + * com.sun.star.beans.XFastPropertySet and com.sun.star.beans.XMultiPropertySet. This + * class has to be inherited to be used. The values of properties are stored in member + * variables of the inheriting class. By overriding the methods + * {@link #convertPropertyValue convertPropertyValue}, + * {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and + * {@link #getPropertyValue(Property)} one can determine how + * property values are stored. + * When using the supplied implementations of this class then the member variables which + * hold property values have to be declared in the class which inherits last in the inheriting + * chain and they have to be public<p> + * Properties have to be registered by one of the registerProperty methods. They take among other + * arguments an Object named <em>id</em> which has to be a String that represents the name of + * the member variable. The registering has to occur in the constructor of the inheriting class. + * It is no allowed to add or change properties later on.<p> + * Example: + * <pre> + * public class Foo extends PropertySet + * { + * protected int intProp; + * + * public Foo() + * { + * registerProperty("PropertyA", 0, new Type(int.class), (short)0, "intProp"); + * } + * } + * + * </pre> + */ +public class PropertySet extends ComponentBase implements XPropertySet, XFastPropertySet, +XMultiPropertySet +{ + private HashMap<String,Property> _nameToPropertyMap; + private HashMap<Integer,Property> _handleToPropertyMap; + private HashMap<Property,Object> _propertyToIdMap; + private Property[] arProperties; + + private int lastHandle= 1; + + protected XPropertySetInfo propertySetInfo; + protected MultiTypeInterfaceContainer aBoundLC= new MultiTypeInterfaceContainer(); + protected MultiTypeInterfaceContainer aVetoableLC= new MultiTypeInterfaceContainer(); + public PropertySet() + { + super(); + initMappings(); + } + + /** Registers a property with this helper class and associates the argument <em>id</em> with it. + * <em>id</em> is used to identify the storage of the property value. How property values are stored + * and retrieved is determined by the methods {@link #convertPropertyValue convertPropertyValue}, + * {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and {@link #getPropertyValue(Property) getPropertyValue} + * These methods expect <em>id</em> to be a String which represents the name of a member variable + * which holds the property value. + * Only properties which are registered can be accessed. Registration has to occur during + * initialization of the inheriting class (i.e. within the constructor). + * @param prop The property to be registered. + * @param id Identifies the properties storage. + * @see #getPropertyId + */ + protected void registerProperty(Property prop, Object id) + { + putProperty(prop); + assignPropertyId(prop, id); + } + + /** Registers a property with this helper class and associates the argument id with it. + * It does the same as {@link #registerProperty(Property, Object)}. The first four + * arguments are used to construct a Property object. + * Registration has to occur during + * initialization of the inheriting class (i.e. within the constructor) + * @param name The property's name (Property.Name). + * @param handle The property's handle (Property.Handle). + * @param type The property's type (Property.Type). + * @param attributes The property's attributes (Property.Attributes). + * @param id Identifies the property's storage. + */ + protected void registerProperty(String name, int handle, Type type, short attributes, Object id) + { + Property p= new Property(name, handle, type, attributes); + registerProperty(p, id); + } + + /** Registers a property with this class and associates the argument id with it. + * It does the same as {@link #registerProperty(Property, Object)}. The first three + * arguments are used to construct a Property object. The value for the Property.Handle + * is generated and does not have to be specified here. Use this method for registering + * a property if you do not care about the Property's handles. + * Registration has to occur during + * initialization of the inheriting class (i.e. within the constructor). + * @param name The property's name (Property.Name). + * @param type The property's type (Property.Type). + * @param attributes The property's attributes (Property.Attributes). + * @param id Identifies the property's storage. + */ + protected void registerProperty(String name, Type type, short attributes, Object id) + { + Property p= new Property(name, lastHandle++, type, attributes); + registerProperty(p, id); + } + + /** Registers a property with this class. This method expects that property values + * are stored in member variables as is the case if the methods convertPropertyValue, + * setPropertyValueNoBroadcast and getPropertyValue(Property) are not overridden. + * It is presumed that the type of the member variable + * corresponds Property.Type. For example, if the TypeClass of Property.Type is to be + * a TypeClass.SHORT then the member must be a short or java.lang.Short. + * The handle for the property is generated.<br> + * If there is no member with the specified name or if the member has an incompatible type + * then a com.sun.star.uno.RuntimeException is thrown. + * @param propertyName The name of the property. + * @param memberName The name of the member variable that holds the value of the property. + * @param attributes The property attributes. + */ + protected void registerProperty(String propertyName, String memberName, short attributes) + { + Field propField= null; + try + { + propField= getClass().getDeclaredField(memberName); + } + catch (NoSuchFieldException e) + { + throw new com.sun.star.uno.RuntimeException(e, "there is no member variable: " + memberName); + } + Class cl= propField.getType(); + Type t= new Type(cl); + if (t.getTypeClass() != TypeClass.UNKNOWN) + { + Property p= new Property(propertyName, lastHandle++, t, attributes); + registerProperty(p,memberName); + } + else + throw new com.sun.star.uno.RuntimeException("the member has an unknown type: " + memberName); + } + + /** Registers a property with this class. + * It is presumed that the name of property is equal to the name of the member variable + * that holds the property value. + * @param propertyName The name of the property and the member variable that holds the property's value. + * @param attributes The property attributes. + * @see #registerProperty(String, String, short) + */ + protected void registerProperty(String propertyName, short attributes) + { + registerProperty(propertyName, propertyName, attributes); + } + + + + /** Returns the Property object for a given property name or null if that property does + * not exists (i.e. it has not been registered). Override this method + * if you want to implement your own mapping from property names to Property objects. + * Then you also have to override {@link #initMappings}, {@link #getProperties()} and + * {@link #putProperty(Property)}. + * @param propertyName The name of the property (Property.Name) + * @return The Property object with the name <em>propertyName</em>. + */ + protected Property getProperty(String propertyName) + { + return _nameToPropertyMap.get(propertyName); + } + + /** Returns the Property object with a handle (Property.Handle) as specified by the argument + * <em>nHandle</em>. The method returns null if there is no such property (i.e. it has not + * been registered). Override this method if you want to implement your own mapping from handles + * to Property objects. Then you also have to override {@link #initMappings}, {@link #putProperty(Property)}. + * @param nHandle The handle of the property (Property.Handle). + * @return The Property object with the handle <em>nHandle</em> + */ + protected Property getPropertyByHandle(int nHandle) + { + return _handleToPropertyMap.get(Integer.valueOf(nHandle)); + } + + /** Returns an array of all Property objects or an array of length null if there + * are no properties. Override this method if you want to implement your own mapping from names + * to Property objects. Then you also have to override {@link #initMappings}, {@link #getProperty(String)} and + * {@link #putProperty}. + * @return Array of all Property objects. + */ + protected Property[] getProperties() + { + if (arProperties == null) + { + Collection<Property> values= _nameToPropertyMap.values(); + arProperties= values.toArray(new Property[_nameToPropertyMap.size()]); + } + return arProperties; + } + + /** Stores a Property object so that it can be retrieved subsequently by + * {@link #getProperty(String)},{@link #getProperties()},{@link #getPropertyByHandle(int)}. + * Override this method if you want to implement your own mapping from handles + * to Property objects and names to Property objects. Then you also need to override {@link #initMappings}, + * {@link #getProperty(String)},{@link #getProperties()},{@link #getPropertyByHandle(int)}. + * @param prop The Property object that is to be stored. + */ + protected void putProperty(Property prop) + { + _nameToPropertyMap.put(prop.Name, prop); + if (prop.Handle != -1) + _handleToPropertyMap.put(Integer.valueOf(prop.Handle), prop); + } + + /** Assigns an identifier object to a Property object so that the identifier + * can be obtained by {@link #getPropertyId getPropertyId} later on. The identifier + * is used to specify a certain storage for the property's value. If you do not + * override {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} or {@link #getPropertyValue(Property)} + * then the argument <em>id</em> has to be a String that equals the name of + * the member variable that holds the Property's value. + * Override this method if you want to implement your own mapping from Property objects to ids or + * if you need ids of a type other than String. + * Then you also need to override {@link #initMappings initMappings} and {@link #getPropertyId getPropertyId}. + * @param prop The Property object that is being assigned an id. + * @param id The object which identifies the storage used for the property's value. + * @see #registerProperty(Property, Object) + */ + protected void assignPropertyId(Property prop, Object id) + { + if (id instanceof String && ((String) id).length() != 0) + _propertyToIdMap.put(prop, id); + } + + /** Returns the identifier object for a certain Property. The object must have been + * previously assigned to the Property object by {@link #assignPropertyId assignPropertyId}. + * Override this method if you want to implement your own mapping from Property objects to ids. + * Then you also need to override {@link #initMappings initMappings} and {@link #assignPropertyId assignPropertyId}. + * @param prop The property for which the id is to be retrieved. + * @return The id object that identifies the storage used for the property's value. + * @see #registerProperty(Property, Object) + */ + protected Object getPropertyId(Property prop) + { + return _propertyToIdMap.get(prop); + } + + /** Initializes data structures used for mappings of property names to property object, + * property handles to property objects and property objects to id objects. + * Override this method if you want to implement your own mappings. Then you also need to + * override {@link #putProperty putProperty},{@link #getProperty getProperty}, {@link #getPropertyByHandle}, + * {@link #assignPropertyId assignPropertyId} and {@link #getPropertyId getPropertyId}. + */ + protected void initMappings() + { + _nameToPropertyMap= new HashMap<String,Property>(); + _handleToPropertyMap= new HashMap<Integer,Property>(); + _propertyToIdMap= new HashMap<Property,Object>(); + } + + /** Makes sure that listeners which are kept in aBoundLC (XPropertyChangeListener) and aVetoableLC + * (XVetoableChangeListener) receive a disposing call. Also those listeners are released. + */ + @Override + protected void postDisposing() + { + // Create an event with this as sender + EventObject aEvt= new EventObject(this); + + // inform all listeners to release this object + aBoundLC.disposeAndClear(aEvt); + aVetoableLC.disposeAndClear(aEvt); + } + + //XPropertySet ---------------------------------------------------- + synchronized public void addPropertyChangeListener(String str, XPropertyChangeListener xPropertyChangeListener) + throws UnknownPropertyException, WrappedTargetException + { + // only add listeners if you are not disposed + if (! bInDispose && ! bDisposed) + { + if (str.length() > 0) + { + Property prop= getProperty(str); + if (prop == null) + throw new UnknownPropertyException("Property " + str + " is unknown"); + + // Add listener for a certain property + if ((prop.Attributes & PropertyAttribute.BOUND) > 0) + aBoundLC.addInterface(str, xPropertyChangeListener); + else + //ignore silently + return; + } + else + // Add listener for all properties + listenerContainer.addInterface(XPropertyChangeListener.class, xPropertyChangeListener); + } + } + //XPropertySet ---------------------------------------------------- + synchronized public void addVetoableChangeListener(String str, com.sun.star.beans.XVetoableChangeListener xVetoableChangeListener) throws com.sun.star.beans.UnknownPropertyException, com.sun.star.lang.WrappedTargetException + { + // only add listeners if you are not disposed + if (! bInDispose && ! bDisposed) + { + if (str.length() > 0) + { + Property prop= getProperty(str); + if (prop == null) + throw new UnknownPropertyException("Property " + str + " is unknown"); + + // Add listener for a certain property + if ((prop.Attributes & PropertyAttribute.CONSTRAINED) > 0) + aVetoableLC.addInterface(str, xVetoableChangeListener); + else + //ignore silently + return; + } + else + // Add listener for all properties + listenerContainer.addInterface(XVetoableChangeListener.class, xVetoableChangeListener); + } + } + //XPropertySet ---------------------------------------------------- + public synchronized com.sun.star.beans.XPropertySetInfo getPropertySetInfo() + { + if (propertySetInfo == null) + propertySetInfo= new PropertySetInfo(); + return propertySetInfo; + } + //XPropertySet ---------------------------------------------------- + public Object getPropertyValue(String name) throws UnknownPropertyException, WrappedTargetException + { + Object ret= null; + if (bInDispose || bDisposed) + throw new com.sun.star.lang.DisposedException("The component has been disposed already"); + + Property prop= getProperty(name); + if (prop == null) + throw new UnknownPropertyException("The property " + name + " is unknown"); + + synchronized (this) + { + ret= getPropertyValue(prop); + } + // null must not be returned. Either a void any is returned or an any containing + // an interface type and a null reference. + if (ret == null) + { + if (prop.Type.getTypeClass() == TypeClass.INTERFACE) + ret= new Any(prop.Type, null); + else + ret= new Any(new Type(void.class), null); + } + return ret; + } + + //XPropertySet ---------------------------------------------------- + synchronized public void removePropertyChangeListener(String propName, XPropertyChangeListener listener) throws UnknownPropertyException, WrappedTargetException + { // all listeners are automatically released in a dispose call + if (!bInDispose && !bDisposed) + { + if (propName.length() > 0) + { + Property prop = getProperty(propName); + if (prop == null) + throw new UnknownPropertyException("Property " + propName + " is unknown"); + aBoundLC.removeInterface(propName, listener); + } + else + listenerContainer.removeInterface(XPropertyChangeListener.class, listener); + } + } + + //XPropertySet ---------------------------------------------------- + synchronized public void removeVetoableChangeListener(String propName, XVetoableChangeListener listener) throws UnknownPropertyException, WrappedTargetException + {// all listeners are automatically released in a dispose call + if (!bInDispose && !bDisposed) + { + if (propName.length() > 0) + { + Property prop = getProperty(propName); + if (prop == null) + throw new UnknownPropertyException("Property " + propName + " is unknown"); + aVetoableLC.removeInterface(propName, listener); + } + else + listenerContainer.removeInterface(XVetoableChangeListener.class, listener); + } + } + + //XPropertySet ---------------------------------------------------- + /** Sets the value of a property. + * The idl description for this interfaces, stipulates that the argument value is an Any. Since a java.lang.Object + * reference has the same meaning as an Any this function accepts + * java anys (com.sun.star.uno.Any) and all other appropriate objects as arguments. The value argument can be one + * of these: + * <ul> + * <li>java.lang.Boolean</li> + * <li>java.lang.Character</li> + * <li>java.lang.Byte</li> + * <li>java.lang.Short</li> + * <li>java.lang.Integer</li> + * <li>java.lang.Long</li> + * <li>java.lang.Float</li> + * <li>java.lang.Double</li> + * <li>String</li> + * <li>com.sun.star.uno.Type</li> + * <li><em>objects which implement UNO interfaces</em></li> + * <li><em>arrays which contain elements of the types above</em></li> + * <li>com.sun.star.uno.Any containing an instance of one of the above types</li> + * </ul> + * + * Properties can have the attribute com.sun.star.beans.PropertyAttribute.MAYBEVOID, which means that the value + * (not the type) can be void. In order to assign a void value to a property one can either pass an Any which + * contains a null reference or pass null directly. In both cases the null reference is only accepted if + * the PropertyAttribute.MAYBEVOID attribute is set for the property. + * + * Properties which have the attribute MAYBEVOID set (Property.Attributes) can have a void value. The following + * considerations presume that the Property has that attribute set. Further, when mentioning an Any's value we + * actually refer to the object returned by Any.getObject. + * If the argument <em>value</em> is null, or it is an Any whose value is null (but with a valid Type) + * then the member variable used for storing the property's value is set to null. + * Therefore those properties can only be stored in objects + * and primitive types are not allowed (one can use the wrapper classes instead,e.g. java.lang.Byte) . + * If a property's value is kept in a member variable of type Any and that reference is still null + * then when setPropertyValue is called with + * <em>value</em> = null then the member variable is assigned an Any with type void and a null value. + * Or if the argument is an Any with a null value then it is assigned to the member variable. + * Further, if the variable already + * references an Any and setPropertyValue is called with <em>value</em> = null, then the variable is assigned + * a new Any with the same type as the previously referenced Any and with a null value. + * @param name The name of the property. + * @param value The new value of the property. + * * */ + public void setPropertyValue(String name, Object value) throws UnknownPropertyException, + PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + Property prop= getProperty(name); + if (prop == null) + throw new UnknownPropertyException("Property " + name + " is unknown"); + setPropertyValue(prop, value); + } + + /** Sets the value of a property. It checks if the property's attributes (READONLY,MAYBEVOID), allow that the + * new value can be set. It also causes the notification of listeners. + * @param prop The property whose value is to be set. + * @param value The new value for the property. + * + * @throws UnknownPropertyException + * See com.sun.star.beans.XPropertySet + * @throws PropertyVetoException + * See com.sun.star.beans.XPropertySet + * @throws WrappedTargetException + * See com.sun.star.beans.XPropertySet + */ + protected void setPropertyValue(Property prop, Object value) throws UnknownPropertyException, + PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + if ((prop.Attributes & PropertyAttribute.READONLY) == PropertyAttribute.READONLY) + throw new com.sun.star.beans.PropertyVetoException(); + // The value may be null only if MAYBEVOID attribute is set + boolean bVoidValue; + if (value instanceof Any) + bVoidValue= ((Any) value).getObject() == null; + else + bVoidValue= value == null; + if (bVoidValue && (prop.Attributes & PropertyAttribute.MAYBEVOID) == 0) + throw new com.sun.star.lang.IllegalArgumentException("The property must have a value; the MAYBEVOID attribute is not set!"); + if (bInDispose || bDisposed) + throw new DisposedException("Component is already disposed"); + + //Check if the argument is allowed + boolean bValueOk; + if (value instanceof Any) + bValueOk= checkType(((Any) value).getObject()); + else + bValueOk= checkType(value); + if (! bValueOk) + throw new com.sun.star.lang.IllegalArgumentException("No valid UNO type"); + + + boolean bConversionOk= false; + Object[] outConvertedVal= new Object[1]; + Object[] outOldValue= new Object[1]; + synchronized (this) + { + bConversionOk= convertPropertyValue(prop, outConvertedVal, outOldValue, value); + } + + //The next step following the conversion is to set the new value of the property. Prior to this + // the XVetoableChangeListener s have to be notified. + if (bConversionOk) + { + // If the property is CONSTRAINED, then we must notify XVetoableChangeListener. The listener can throw a com.sun.star.lang.beans.PropertyVetoException which + // will cause this method to return (the exception is not caught here). + fire( new Property[]{prop}, outConvertedVal, outOldValue, true); + + synchronized (this) + { + setPropertyValueNoBroadcast(prop, outConvertedVal[0]); + } + // fire a change event (XPropertyChangeListener, PropertyAttribute.BOUND + fire( new Property[]{prop}, outConvertedVal, outOldValue, false); + } + } + + /** Converts a value in a way so that it is appropriate for storing as a property value, that is + * {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} can process the value without any further + * conversion. This implementation presumes that + * the values are stored in member variables of the furthest inheriting class. For example, + * class A inherits this class then members of class A + * can hold property values. If there is a class B which inherits A then only members of B can hold + * property values. The variables must be public. A property must have been registered (e.g. by + * {@link #registerProperty(Property, Object)} in order for this method to work. The identifier argument (type Object) + * used in the registerProperty methods must + * be a String, which is, the name of the member variable that holds the property value. + * If one opts to store values differently then one may override + * this method, as well as {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and + * {@link #getPropertyValue(Property) getPropertyValue(Property)}. + * This method is always called as a result of a call to one of the setter methods, such as + * {@link #setPropertyValue(String,Object) XPropertySet.setPropertyValue}, + * {@link #setFastPropertyValue XFastPropertySet.setFastPropertyValue} + * and {@link #setPropertyValues XMultiPropertySet.setPropertyValues}. + * If this method fails, that is, it returns false or throws an exception, then no listeners are notified and the + * property value, that was intended to be changed, remains untouched. + * + * This method does not have to deal with property attributes, such as + * PropertyAttribute.READONLY or PropertyAttribute.MAYBEVOID. The processing of these attributes occurs + * in the calling methods. + * + * Only if this method returns successfully further processing, such + * as listener notification and finally the modification of the property's value, will occur. + * + * The actual modification of a property's value is done by {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} + * which is called subsequent to convertPropertyValue. + *<p> + * This method converts values by help of the com.sun.star.uno.AnyConverter which only does a few widening + * conversions on integer types and floating point types. For example, there is the property PropA with a Type equivalent + * to int.class and the + * value of the property is to be stored in a member variable of type int with name intProp. Then setPropertyValue is + * called: + * <pre> + * set.setPropertyValue( "PropA", Byte.valueOf( (byte)111)); + * </pre> + * At some point setPropertyValue will call convertPropertyValue and pass in the Byte object. Since we allow + * that Byte values can be used with the property and know that the value is to be stored in intProp (type int) + * we convert the Byte object into an Integer object which is then returned in the out-parameter <em>newVal</em>. This + * conversion is actually performed by the AnyConverter. Later + * the setPropertyValueNoBroadcast is called with that Integer object and the int value can be easily extracted + * from the object and be assigned to the member intProp. + * <p> + * The method handles Any arguments the same as Object arguments. That is, the <em>setVal</em> argument can + * be a java.lang.Boolean or a com.sun.star.uno.Any containing a java.lang.Boolean. Likewise, a member + * containing a property value can be a com.sun.star.uno.Any or a java.lang.Object. + * Then, no conversion is necessary, since they can hold all possible values. However, if + * the member is an Object and <em>setVal</em> is an Any then the object contained in the any is assigned to + * the member. The extra type information which exists as Type object in the Any will get lost. If this is not + * intended then use an Any variable rather than an Object. + * + * If a member is an Object or Any and the argument <em>setVal</em> is an Object, other than String or array, + * then it is presumed to be a UNO object and queried for XInterface. If successful, the out-param <em>newVal</em> + * returns the XInterface. + * + * If a member is a UNO interface, then <em>setVal</em> is queried for this interface and the result is returned. + * If <em>setVal</em> is null then <em>newVal</em> will be null too after return. + * <p> + * If a property value is stored using a primitive type the out-parameters + * <em>curVal</em> and <em>newVal</em> contain the respective wrapper class (e.g.java.lang.Byte, etc.). + * curVal is used in calls to the XVetoableChangeListener and XPropertyChangeListener. + * + * @param property - in-param property for which the data is to be converted. + * @param newVal - out-param which contains the converted value on return. + * @param curVal - out-param the current value of the property. It is used in calls to the + * XVetoableChangeListener and XPropertyChangeListener. + * @param setVal - in-param. The value that is to be converted so that it matches Property and the internally used + * dataformat for that property. + * @return true - Conversion was successful. <em>newVal</em> contains a valid value for the property. false - + * conversion failed for some reason. + * + * @throws UnknownPropertyException + * See com.sun.star.beans.XPropertySet + * @throws com.sun.star.lang.IllegalArgumentException The value provided is unfit for the property. + * @throws com.sun.star.lang.WrappedTargetException - An exception occurred during the conversion, that is to be made known + * to the caller. + */ + protected boolean convertPropertyValue(Property property, Object[] newVal, Object[]curVal, Object setVal) + throws com.sun.star.lang.IllegalArgumentException, WrappedTargetException, UnknownPropertyException + { + boolean ret= true; + try + { + // get the member name + String sMember= (String) getPropertyId(property); + if (sMember != null) + { + // use reflection to obtain the field that holds the property value + // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to + // also get inherited fields, but only those which are public. + Field propField= getClass().getDeclaredField(sMember); + if (propField != null) + { + curVal[0]= propField.get(this); + Class memberClass= propField.getType(); + + // MAYBEVOID: if setVal == null or it is an Any and getObject returns null, then a void value is to be set + // This works only if there are no primitive types. For those we use the respective wrapper classes. + // In this implementation, a null reference means void value. + boolean bVoidValue= false; + boolean bAnyVal= setVal instanceof Any; + if (bAnyVal) + bVoidValue= ((Any) setVal).getObject() == null; + else + bVoidValue= setVal == null; + if (bVoidValue && memberClass.isPrimitive()) + throw new com.sun.star.lang.IllegalArgumentException("The implementation does not support the MAYBEVOID attribute for this property"); + + Object convObj= null; + //The member that keeps the value of the Property is an Any. It can contain all possible + //types, therefore a conversion is not necessary. + if (memberClass.equals(Any.class)) + { + if (bAnyVal) + //parameter setVal is also an Any and can be used without further processing + convObj= setVal; + else + { + // Parameter setVal is not an Any. We need to construct an Any that contains + // the argument setVal. + // If setVal is an interface implementation then, we cannot construct the + // Any with setVal.getClass(), because the Any.Type._typeClass would be TypeClass.UNKNOWN. + // We try to get an XInterface of setVal and set an XInterface type. + if (setVal instanceof XInterface) + { + XInterface xint= UnoRuntime.queryInterface(XInterface.class, setVal); + if (xint != null) + convObj= new Any(new Type(XInterface.class), xint); + } + // The member is an any, and the past in argument was null reference (MAYBEVOID is set) + else if (setVal == null) + { + // if the any member is still null we create a void any + if (curVal[0] == null) + convObj= new Any(new Type(), null); + else + { + //otherwise we create an Any with the same type as a value of null; + convObj= new Any( ((Any)curVal[0]).getType(), null); + } + } + else + convObj= new Any(new Type(setVal.getClass()), setVal); + } + } + else + convObj= convert(memberClass, setVal); + newVal[0]= convObj; + } + } + else + throw new UnknownPropertyException("Property " + property.Name + " is unknown"); + } + catch (java.lang.NoSuchFieldException e) + { + throw new WrappedTargetException(e, "Field does not exist", this, e); + } + catch (java.lang.IllegalAccessException e) + { + throw new WrappedTargetException(e, "", this ,e); + } + return ret; + } + + private boolean checkType(Object obj) + { + return obj == null + || obj instanceof Boolean + || obj instanceof Character + || obj instanceof Number + || obj instanceof String + || obj instanceof XInterface + || obj instanceof Type + || obj instanceof com.sun.star.uno.Enum + || obj.getClass().isArray(); + } + + // Param object can be an Any or other object. If obj is null then the return value is null + private Object convert( Class cl, Object obj) throws com.sun.star.lang.IllegalArgumentException + { + Object retVal= null; + //The member that keeps the value of the Property is an Object.Objects are similar to Anys in that they can + // hold all types. + if (obj == null || (obj instanceof Any && ((Any) obj).getObject() == null)) + {} + else if(cl.equals(Object.class)) + { + if (obj instanceof Any) + obj= ((Any) obj).getObject(); + retVal= obj; + } + else if(cl.equals(boolean.class)) + retVal= Boolean.valueOf(AnyConverter.toBoolean(obj)); + else if (cl.equals(char.class)) + retVal= new Character(AnyConverter.toChar(obj)); + else if (cl.equals(byte.class)) + retVal= Byte.valueOf(AnyConverter.toByte(obj)); + else if (cl.equals(short.class)) + retVal= Short.valueOf(AnyConverter.toShort(obj)); + else if (cl.equals(int.class)) + retVal= Integer.valueOf(AnyConverter.toInt(obj)); + else if (cl.equals(long.class)) + retVal= Long.valueOf(AnyConverter.toLong(obj)); + else if (cl.equals(float.class)) + retVal= new Float(AnyConverter.toFloat(obj)); + else if (cl.equals(double.class)) + retVal= new Double(AnyConverter.toDouble(obj)); + else if (cl.equals(String.class)) + retVal= AnyConverter.toString(obj); + else if (cl.isArray()) + retVal= AnyConverter.toArray(obj); + else if (cl.equals(Type.class)) + retVal= AnyConverter.toType(obj); + else if (cl.equals(Boolean.class)) + retVal= Boolean.valueOf(AnyConverter.toBoolean(obj)); + else if (cl.equals(Character.class)) + retVal= new Character(AnyConverter.toChar(obj)); + else if (cl.equals(Byte.class)) + retVal= Byte.valueOf(AnyConverter.toByte(obj)); + else if (cl.equals(Short.class)) + retVal= Short.valueOf(AnyConverter.toShort(obj)); + else if (cl.equals(Integer.class)) + retVal= Integer.valueOf(AnyConverter.toInt(obj)); + else if (cl.equals(Long.class)) + retVal= Long.valueOf(AnyConverter.toLong(obj)); + else if (cl.equals(Float.class)) + retVal= new Float(AnyConverter.toFloat(obj)); + else if (cl.equals(Double.class)) + retVal= new Double(AnyConverter.toDouble(obj)); + else if (XInterface.class.isAssignableFrom(cl)) + retVal= AnyConverter.toObject(new Type(cl), obj); + else if (com.sun.star.uno.Enum.class.isAssignableFrom(cl)) + retVal= AnyConverter.toObject(new Type(cl), obj); + else + throw new com.sun.star.lang.IllegalArgumentException("Could not convert the argument"); + return retVal; + } + + /** Sets the value of a property. In this implementation property values are stored in member variables + * (see {@link #convertPropertyValue convertPropertyValue} Notification of property listeners + * does not occur in this method. By overriding this method one can take full control about how property values + * are stored. But then, the {@link #convertPropertyValue convertPropertyValue} and + * {@link #getPropertyValue(Property)} must be overridden too. + * + * A Property with the MAYBEVOID attribute set, is stored as null value. Therefore the member variable must be + * an Object in order to make use of the property attribute. An exception is Any. The Any variable can be initially null, but + * once it is set the reference will not become null again. If the value is to be set to + * void then a new Any will be stored + * with a valid type but without a value (i.e. Any.getObject returns null). + * If a property has the READONLY attribute set, and one of the setter methods, such as setPropertyValue, has been + * called, then this method is not going to be called. + * @param property the property for which the new value is set + * @param newVal the new value for the property. + * @throws com.sun.star.lang.WrappedTargetException An exception, which has to be made known to the caller, + * occurred during the setting of the value. + */ + protected void setPropertyValueNoBroadcast(Property property, Object newVal) + throws WrappedTargetException + { + try + { + // get the member name + String sMember= (String) getPropertyId(property); + if (sMember != null) + { + // use reflection to obtain the field that holds the property value + // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to + // also get inherited fields, but only those which are public. + Field propField= getClass().getDeclaredField(sMember); + if (propField != null) + propField.set(this, newVal); + } + } + catch(java.lang.Exception e) + { + throw new WrappedTargetException(e, "PropertySet.setPropertyValueNoBroadcast", this, e); + } + } + /** Retrieves the value of a property. This implementation presumes that the values are stored in member variables + * of the furthest inheriting class (see {@link #convertPropertyValue convertPropertyValue}) and that the + * variables are public. The property must have + * been registered, for example by {@link #registerProperty(Property, Object)}. The identifier Object argument + * must have been a String which was the name of the member variable holding the property value. + * When properties are to be stored differently one has to override this method as well as + * {@link #convertPropertyValue} and {@link #setPropertyValueNoBroadcast}. <br> + * If a value is stored in a variable of a primitive type then this method returns an instance of the respective + * wrapper class (e.g. java.lang.Boolean). + * @param property The property for which the value is to be retrieved. + * @return The value of the property. + */ + protected Object getPropertyValue(Property property) + { + Object ret= null; + try + { + // get the member name + String sMember= (String) getPropertyId(property); + if (sMember != null) + { + // use reflection to obtain the field that holds the property value + // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to + // also get inherited fields, but only those which are public. + Field propField= getClass().getDeclaredField(sMember); + if (propField != null) + ret= propField.get(this); + } + } + catch(java.lang.NoSuchFieldException e) + { + throw new java.lang.RuntimeException(e); + } + catch(java.lang.IllegalAccessException e) + { + throw new java.lang.RuntimeException(e); + } + return ret; + } + + /** + * This method fires events to XPropertyChangeListener,XVetoableChangeListener and + * XPropertiesChangeListener event sinks. + * To distinguish what listeners are to be called the argument <em>bVetoable</em> is to be set to true if + * a XVetoableChangeListener is meant. For XPropertyChangeListener and XPropertiesChangeListener + * it is to be set to false. + * + * @param properties Properties which will be or have been affected. + * @param newValues the new values of the properties. + * @param oldValues the old values of the properties. + * @param bVetoable true means fire to VetoableChangeListener, false means fire to + * XPropertyChangedListener and XMultiPropertyChangedListener. + * + * @throws PropertyVetoException + * if a vetoable listener throws it. + */ + protected void fire( + Property[] properties, + Object[] newValues, + Object[] oldValues, + boolean bVetoable ) throws PropertyVetoException + { + // Only fire, if one or more properties changed + int nNumProps= properties.length; + if (nNumProps > 0) + { + PropertyChangeEvent[] arEvts= new PropertyChangeEvent[nNumProps]; + int nAffectedProps= 0; + // Loop over all changed properties to fill the event struct + for (int i= 0; i < nNumProps; i++) + { + if ((bVetoable && (properties[i].Attributes & PropertyAttribute.CONSTRAINED) > 0) + || (!bVetoable && (properties[i].Attributes & PropertyAttribute.BOUND) > 0)) + { + arEvts[i]= new PropertyChangeEvent(this, properties[i].Name, false, + properties[i].Handle, oldValues[i], newValues[i]); + nAffectedProps++; + } + } + // fire the events for all changed properties + for (int i= 0; i < nAffectedProps; i++) + { + // get the listener container for the property name + InterfaceContainer lc; + if (bVetoable) + lc= aVetoableLC.getContainer(arEvts[i].PropertyName); + else + lc= aBoundLC.getContainer(arEvts[i].PropertyName); + Iterator it = lc != null ? lc.iterator() : null; + if (it != null) + { + while( it.hasNext()) + { + Object listener= it.next(); + if (bVetoable) + ((XVetoableChangeListener) listener).vetoableChange(arEvts[i]); + else + ((XPropertyChangeListener) listener).propertyChange(arEvts[i]); + } + } + // broadcast to all listeners with "" property name + if(bVetoable) + lc= listenerContainer.getContainer(XVetoableChangeListener.class); + else + lc= listenerContainer.getContainer(XPropertyChangeListener.class); + it = lc != null ? lc.iterator() : null; + if (it != null) + { + while(it.hasNext() ) + { + Object listener= it.next(); + if( bVetoable ) // fire change Events? + ((XVetoableChangeListener) listener).vetoableChange(arEvts[i]); + else + ((XPropertyChangeListener) listener).propertyChange(arEvts[i]); + } + } + } + // fire at XPropertiesChangeListeners + // if nAffectedProps == 0 then there are no BOUND properties + if (!bVetoable && nAffectedProps > 0) + { + + PropertyChangeEvent[] arReduced= new PropertyChangeEvent[nAffectedProps]; + System.arraycopy(arEvts, 0, arReduced, 0, nAffectedProps); + InterfaceContainer lc= listenerContainer.getContainer(XPropertiesChangeListener.class); + Iterator it = lc != null ? lc.iterator() : null; + if (it != null) + { + while (it.hasNext()) + { + XPropertiesChangeListener listener = (XPropertiesChangeListener) it.next(); + // fire the whole event sequence to the XPropertiesChangeListener's + listener.propertiesChange( arEvts ); + } + } + } + } + } + // XFastPropertySet-------------------------------------------------------------------------------- + public void setFastPropertyValue(int nHandle, Object aValue ) throws UnknownPropertyException, + PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + Property prop= getPropertyByHandle(nHandle); + if (prop == null) + throw new UnknownPropertyException(" The property with handle : " + nHandle +" is unknown"); + setPropertyValue(prop, aValue); + } + + // XFastPropertySet -------------------------------------------------------------------------------- + public Object getFastPropertyValue(int nHandle ) throws UnknownPropertyException, + WrappedTargetException + { + Property prop= getPropertyByHandle(nHandle); + if (prop == null) + throw new UnknownPropertyException("The property with handle : " + nHandle + " is unknown"); + return getPropertyValue(prop); + } + + // XMultiPropertySet ----------------------------------------------------------------------------------- + public void addPropertiesChangeListener(String[] propNames, XPropertiesChangeListener listener) + { + listenerContainer.addInterface(XPropertiesChangeListener.class, listener); + } + + // XMultiPropertySet ----------------------------------------------------------------------------------- + public void firePropertiesChangeEvent(String[] propNames, XPropertiesChangeListener listener) + { + // Build the events. + PropertyChangeEvent[] arEvents= new PropertyChangeEvent[propNames.length]; + int eventCount= 0; + // get a snapshot of the current property values + synchronized (this) + { + for (int i= 0; i < propNames.length; i++) + { + Property prop= getProperty(propNames[i]); + if (prop != null) + { + Object value= null; + try + { + value= getPropertyValue(prop); + } + catch(Exception e) + { + continue; + } + arEvents[eventCount]= new PropertyChangeEvent(this, prop.Name, + false, prop.Handle, value, value); + eventCount++; + } + } + } + + // fire events from unsynchronized section so as to prevent deadlocks + if (eventCount > 0) + { + // Reallocate the array of the events if necessary + if (arEvents.length != eventCount) + { + PropertyChangeEvent[] arPropsTmp= new PropertyChangeEvent[eventCount]; + System.arraycopy(arEvents, 0, arPropsTmp, 0, eventCount); + arEvents= arPropsTmp; + } + listener.propertiesChange(arEvents); + } + } + // XMultiPropertySet ----------------------------------------------------------------------------------- + /** If a value for a property could not be retrieved then the respective element in the returned + * array has the value null. + */ + public Object[] getPropertyValues(String[] propNames) + { + Object[] arValues= new Object[propNames.length]; + synchronized (this) + { + for (int i= 0; i < propNames.length; i++) + { + Object value= null; + try + { + value= getPropertyValue(propNames[i]); + } + catch (Exception e) + { + } + arValues[i]= value; + } + } + return arValues; + } + // XMultiPropertySet ----------------------------------------------------------------------------------- + public void removePropertiesChangeListener(XPropertiesChangeListener xPropertiesChangeListener) + { + listenerContainer.removeInterface(XPropertiesChangeListener.class, xPropertiesChangeListener); + } + // XMultiPropertySet ----------------------------------------------------------------------------------- + /** If the array of property names contains an unknown property then it will be ignored. + */ + public void setPropertyValues(String[] propNames, Object[] values) throws PropertyVetoException, com.sun.star.lang.IllegalArgumentException, com.sun.star.lang.WrappedTargetException + { + for (int i= 0; i < propNames.length; i++) + { + try + { + setPropertyValue(propNames[i], values[i]); + } + catch (UnknownPropertyException e) + { + continue; + } + + } + } + + private class PropertySetInfo implements XPropertySetInfo + { + public com.sun.star.beans.Property[] getProperties() + { + return PropertySet.this.getProperties(); + } + + public com.sun.star.beans.Property getPropertyByName(String name) throws UnknownPropertyException + { + return getProperty(name); + } + + public boolean hasPropertyByName(String name) + { + return getProperty(name) != null; + } + + } +} + + + + + diff --git a/ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java b/ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java new file mode 100644 index 000000000..0c050d676 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java @@ -0,0 +1,1111 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.helper; + +import com.sun.star.beans.Property; +import com.sun.star.beans.PropertyAttribute; +import com.sun.star.beans.PropertyChangeEvent; +import com.sun.star.beans.PropertyState; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.PropertyVetoException; +import com.sun.star.beans.UnknownPropertyException; +import com.sun.star.beans.XPropertyChangeListener; +import com.sun.star.beans.XPropertySetInfo; +import com.sun.star.beans.XVetoableChangeListener; +import com.sun.star.container.NoSuchElementException; +import com.sun.star.container.XHierarchicalNameAccess; +import com.sun.star.lang.DisposedException; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.WrappedTargetRuntimeException; +import com.sun.star.reflection.XCompoundTypeDescription; +import com.sun.star.reflection.XIdlClass; +import com.sun.star.reflection.XIdlField2; +import com.sun.star.reflection.XIndirectTypeDescription; +import com.sun.star.reflection.XInterfaceAttributeTypeDescription2; +import com.sun.star.reflection.XInterfaceMemberTypeDescription; +import com.sun.star.reflection.XInterfaceTypeDescription2; +import com.sun.star.reflection.XStructTypeDescription; +import com.sun.star.reflection.XTypeDescription; +import com.sun.star.reflection.theCoreReflection; +import com.sun.star.uno.Any; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.TypeClass; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.uno.XInterface; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +/** + A helper mixin to implement certain UNO interfaces related to property set + handling on top of the attributes of a given UNO interface type. + + <p>A client will mix in this class by keeping a reference to an instance of + this class, and forwarding all methods of (a subset of the interfaces) + <code>com.sun.star.beans.XPropertySet</code>, + <code>com.sun.star.beans.XFastPropertySet</code>, and + <code>com.sun.star.beans.XPropertyAccess</code> to it.</p> + + <p>Client code should not use the monitors associated with instances of this + class, as they are used for internal purposes.</p> + + @since UDK 3.2 +*/ +public final class PropertySetMixin { + /** + The constructor. + + @param context the component context used by this instance; must not be + null, and must supply the + <code>com.sun.star.reflection.theCoreReflection</code> and + <code>com.sun.star.reflection.theTypeDescriptionManager</code> singletons + + @param object the client UNO object into which this instance is mixed in; + must not be null, and must support the given <code>type</code> + + @param type the UNO interface type whose attributes are mapped to + properties; must not be null, and must represent a UNO interface type + + @param absentOptional a list of optional properties that are not present, + and should thus not be visible via + <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code>, + <code>com.sun.star.beans.XPropertySet.addPropertyChangeListener</code>, + <code>com.sun.star.beans.XPropertySet.removePropertyChangeListener<!-- + --></code>, + <code>com.sun.star.beans.XPropertySet.addVetoableChangeListener</code>, + and <code>com.sun.star.beans.XPropertySet.<!-- + -->removeVetoableChangeListener</code>; null is treated the same as an + empty list; if non-null, the given array must not be modified after it is + passed to this constructor. For consistency reasons, the given + <code>absentOptional</code> should only contain the names of attributes + that represent optional properties that are not present (that is, the + attribute getters and setters always throw a + <code>com.sun.star.beans.UnknownPropertyException</code>), and should + contain each such name only once. If an optional property is not present + (that is, the corresponding attribute getter and setter always throw a + <code>com.sun.star.beans.UnknownPropertyException</code>) but is not + contained in the given <code>absentOptional</code>, then it will be + visible via + <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code> as a + <code>com.sun.star.beans.Property</code> with a set + <code>com.sun.star.beans.PropertyAttribute.OPTIONAL</code>. If the given + <code>object</code> does not implement + <code>com.sun.star.beans.XPropertySet</code>, then the given + <code>absentOptional</code> is effectively ignored and can be null or + empty. + */ + public PropertySetMixin( + XComponentContext context, XInterface object, Type type, + String[] absentOptional) + { + this.context = context; + this.object = object; + this.type = type; + this.absentOptional = absentOptional; + idlClass = getReflection(type.getTypeName()); + XTypeDescription ifc; + try { + XHierarchicalNameAccess xhna = UnoRuntime.queryInterface( + XHierarchicalNameAccess.class, + context.getValueByName( + "/singletons/com.sun.star.reflection." + + "theTypeDescriptionManager")); + ifc = UnoRuntime.queryInterface( + XTypeDescription.class, + xhna.getByHierarchicalName(type.getTypeName())); + } catch (NoSuchElementException e) { + throw new RuntimeException(e); + } + HashMap<String,PropertyData> map = new HashMap<String,PropertyData>(); + ArrayList<String> handleNames = new ArrayList<String>(); + initProperties(ifc, map, handleNames, new HashSet<String>()); + properties = map; + handleMap = handleNames.toArray(new String[handleNames.size()]); + } + + /** + A method used by clients when implementing UNO interface type attribute + setter functions. + + <p>First, this method checks whether this instance has already been + disposed (see {@link #dispose}), and throws a + <code>com.sun.star.beans.DisposedException</code> if applicable. For a + constrained attribute (whose setter can explicitly raise + <code>com.sun.star.beans.PropertyVetoException</code>), this method + notifies any <code>com.sun.star.beans.XVetoableChangeListener</code>s. + For a bound attribute, this method modifies the passed-in + <code>bound</code> so that it can afterwards be used to notify any + <code>com.sun.star.beans.XPropertyChangeListener</code>s. This method + should be called before storing the new attribute value, and + <code>bound.notifyListeners()</code> should be called exactly once after + storing the new attribute value (in case the attribute is bound; + otherwise, calling <code>bound.notifyListeners()</code> is ignored). + Furthermore, <code>bound.notifyListeners()</code> and this method have to + be called from the same thread.</p> + + @param propertyName the name of the property (which is the same as the + name of the attribute that is going to be set) + + @param oldValue the property value corresponding to the old attribute + value. This is only used as + <code>com.sun.star.beans.PropertyChangeEvent.OldValue</code>, which is + rather useless, anyway (see “Using the Observer Pattern” in + <a href="http://tools.openoffice.org/CodingGuidelines.sxw"> + <cite>OpenOffice.org Coding Guidelines</cite></a>). If the attribute + that is going to be set is neither bound nor constrained, or if + <code>com.sun.star.beans.PropertyChangeEvent.OldValue</code> should not + be set, {@link Any#VOID} can be used instead. + + @param newValue the property value corresponding to the new + attribute value. This is only used as + <code>com.sun.star.beans.PropertyChangeEvent.NewValue</code>, which is + rather useless, anyway (see “Using the Observer Pattern” in + <a href="http://tools.openoffice.org/CodingGuidelines.sxw"> + <cite>OpenOffice.org Coding Guidelines</cite></a>), <em>unless</em> the + attribute that is going to be set is constrained. If the attribute + that is going to be set is neither bound nor constrained, or if it is + only bound but + <code>com.sun.star.beans.PropertyChangeEvent.NewValue</code> should not + be set, {@link Any#VOID} can be used instead. + + @param bound a reference to a fresh {@link BoundListeners} instance + (which has not been passed to this method before, and on which + {@link BoundListeners#notifyListeners} has not yet been called); may only + be null if the attribute that is going to be set is not bound + + @throws PropertyVetoException if a vetoable listener throws it. + */ + public void prepareSet( + String propertyName, Object oldValue, Object newValue, + BoundListeners bound) + throws PropertyVetoException + { + // assert properties.get(propertyName) != null; + Property p = properties.get(propertyName).property; + ArrayList<XVetoableChangeListener> specificVeto = null; + ArrayList<XVetoableChangeListener> unspecificVeto = null; + synchronized (this) { + if (disposed) { + throw new DisposedException("disposed", object); + } + if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) { + ArrayList<XVetoableChangeListener> o = vetoListeners.get(propertyName); + if (o != null) { + specificVeto = new ArrayList<XVetoableChangeListener>(o); + } + o = vetoListeners.get(""); + if (o != null) { + unspecificVeto = new ArrayList<XVetoableChangeListener>(o); + } + } + if ((p.Attributes & PropertyAttribute.BOUND) != 0) { + // assert bound != null; + ArrayList<XPropertyChangeListener> o = boundListeners.get(propertyName); + if (o != null) { + bound.specificListeners = new ArrayList<XPropertyChangeListener>(o); + } + o = boundListeners.get(""); + if (o != null) { + bound.unspecificListeners = new ArrayList<XPropertyChangeListener>(o); + } + } + } + if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) { + PropertyChangeEvent event = new PropertyChangeEvent( + object, propertyName, false, p.Handle, oldValue, newValue); + if (specificVeto != null) { + for (Iterator<XVetoableChangeListener> i = specificVeto.iterator(); i.hasNext();) { + try { + i.next().vetoableChange(event); + } catch (DisposedException e) {} + } + } + if (unspecificVeto != null) { + for (Iterator<XVetoableChangeListener> i = unspecificVeto.iterator(); i.hasNext();) { + try { + i.next().vetoableChange(event); + } catch (DisposedException e) {} + } + } + } + if ((p.Attributes & PropertyAttribute.BOUND) != 0) { + // assert bound != null; + bound.event = new PropertyChangeEvent( + object, propertyName, false, p.Handle, oldValue, newValue); + } + } + + /** + A simplified version of {@link #prepareSet(String, Object, Object, + PropertySetMixin.BoundListeners)}. + + <p>This method is useful for attributes that are not constrained.</p> + + @param propertyName the name of the property (which is the same as the + name of the attribute that is going to be set) + + @param bound a reference to a fresh {@link BoundListeners} instance + (which has not been passed to this method before, and on which + {@link BoundListeners#notifyListeners} has not yet been called); may only + be null if the attribute that is going to be set is not bound + */ + public void prepareSet(String propertyName, BoundListeners bound) { + try { + prepareSet(propertyName, Any.VOID, Any.VOID, bound); + } catch (PropertyVetoException e) { + throw new RuntimeException("unexpected " + e); + } + } + + /** + Marks this instance as being disposed. + + <p>See <code>com.sun.star.lang.XComponent</code> for the general concept + of disposing UNO objects. On the first call to this method, all + registered listeners + (<code>com.sun.star.beans.XPropertyChangeListener</code>s and + <code>com.sun.star.beans.XVetoableChangeListener</code>s) are notified of + the disposing source. Any subsequent calls to this method are + ignored.</p> + */ + public void dispose() { + HashMap<String,ArrayList<XPropertyChangeListener>> bound; + HashMap<String,ArrayList<XVetoableChangeListener>> veto; + synchronized (this) { + bound = boundListeners; + boundListeners = null; + veto = vetoListeners; + vetoListeners = null; + disposed = true; + } + EventObject event = new EventObject(object); + if (bound != null) { + for (Iterator<ArrayList<XPropertyChangeListener>> i = bound.values().iterator(); i.hasNext();) { + for (Iterator<XPropertyChangeListener> j = i.next().iterator(); j.hasNext();) + { + j.next().disposing(event); + } + } + } + if (veto != null) { + for (Iterator<ArrayList<XVetoableChangeListener>> i = veto.values().iterator(); i.hasNext();) { + for (Iterator<XVetoableChangeListener> j = i.next().iterator(); j.hasNext();) + { + j.next().disposing(event); + } + } + } + } + + /** + Implements + <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code>. + @return See com.sun.star.beans.XPropertySet + */ + public XPropertySetInfo getPropertySetInfo() { + return new Info(properties); + } + + /** + Implements <code>com.sun.star.beans.XPropertySet.setPropertyValue</code>. + @param propertyName + See com.sun.star.beans.XPropertySet + @param value + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws PropertyVetoException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + */ + public void setPropertyValue(String propertyName, Object value) + throws UnknownPropertyException, PropertyVetoException, + com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + setProperty(propertyName, value, false, false, (short) 1); + } + + /** + Implements <code>com.sun.star.beans.XPropertySet.getPropertyValue</code>. + @param propertyName + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + @return + See com.sun.star.beans.XPropertySet + */ + public Object getPropertyValue(String propertyName) + throws UnknownPropertyException, WrappedTargetException + { + return getProperty(propertyName, null); + } + + /** + Implements + <code>com.sun.star.beans.XPropertySet.addPropertyChangeListener</code>. + + <p>If a listener is added more than once, it will receive all relevant + notifications multiple times.</p> + + @param propertyName + See com.sun.star.beans.XPropertySet + @param listener + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + */ + public void addPropertyChangeListener( + String propertyName, XPropertyChangeListener listener) + throws UnknownPropertyException, WrappedTargetException + { + // assert listener != null; + checkUnknown(propertyName); + boolean disp; + synchronized (this) { + disp = disposed; + if (!disp) { + ArrayList<XPropertyChangeListener> v = boundListeners.get(propertyName); + if (v == null) { + v = new ArrayList<XPropertyChangeListener>(); + boundListeners.put(propertyName, v); + } + v.add(listener); + } + } + if (disp) { + listener.disposing(new EventObject(object)); + } + } + + /** + Implements <code> + com.sun.star.beans.XPropertySet.removePropertyChangeListener</code>. + + @param propertyName + See com.sun.star.beans.XPropertySet + @param listener + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + */ + public void removePropertyChangeListener( + String propertyName, XPropertyChangeListener listener) + throws UnknownPropertyException, WrappedTargetException + { + // assert listener != null; + checkUnknown(propertyName); + synchronized (this) { + if (boundListeners != null) { + ArrayList<XPropertyChangeListener> v = boundListeners.get(propertyName); + if (v != null) { + v.remove(listener); + } + } + } + } + + /** + Implements + <code>com.sun.star.beans.XPropertySet.addVetoableChangeListener</code>. + + <p>If a listener is added more than once, it will receive all relevant + notifications multiple times.</p> + + @param propertyName + See com.sun.star.beans.XPropertySet + @param listener + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + */ + public void addVetoableChangeListener( + String propertyName, XVetoableChangeListener listener) + throws UnknownPropertyException, WrappedTargetException + { + // assert listener != null; + checkUnknown(propertyName); + boolean disp; + synchronized (this) { + disp = disposed; + if (!disp) { + ArrayList<XVetoableChangeListener> v = vetoListeners.get(propertyName); + if (v == null) { + v = new ArrayList<XVetoableChangeListener>(); + vetoListeners.put(propertyName, v); + } + v.add(listener); + } + } + if (disp) { + listener.disposing(new EventObject(object)); + } + } + + /** + Implements <code> + com.sun.star.beans.XPropertySet.removeVetoableChangeListener</code>. + + @param propertyName + See com.sun.star.beans.XPropertySet + @param listener + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + */ + public void removeVetoableChangeListener( + String propertyName, XVetoableChangeListener listener) + throws UnknownPropertyException, WrappedTargetException + { + // assert listener != null; + checkUnknown(propertyName); + synchronized (this) { + if (vetoListeners != null) { + ArrayList<XVetoableChangeListener> v = vetoListeners.get(propertyName); + if (v != null) { + v.remove(listener); + } + } + } + } + + /** + Implements + <code>com.sun.star.beans.XFastPropertySet.setFastPropertyValue</code>. + + @param handle + See com.sun.star.beans.XFastPropertySet + @param value + See com.sun.star.beans.XFastPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XFastPropertySet + @throws PropertyVetoException + See com.sun.star.beans.XFastPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XFastPropertySet + */ + public void setFastPropertyValue(int handle, Object value) + throws UnknownPropertyException, PropertyVetoException, + com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + setProperty(translateHandle(handle), value, false, false, (short) 1); + } + + /** + Implements + <code>com.sun.star.beans.XFastPropertySet.getFastPropertyValue</code>. + + @param handle + See com.sun.star.beans.XFastPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XFastPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XFastPropertySet + @return + See com.sun.star.beans.XFastPropertySet + */ + public Object getFastPropertyValue(int handle) + throws UnknownPropertyException, WrappedTargetException + { + return getProperty(translateHandle(handle), null); + } + + /** + Implements + <code>com.sun.star.beans.XPropertyAccess.getPropertyValues</code>. + + @return + See com.sun.star.beans.XPropertyAccess + */ + public PropertyValue[] getPropertyValues() { + PropertyValue[] s = new PropertyValue[handleMap.length]; + int n = 0; + for (int i = 0; i < handleMap.length; ++i) { + PropertyState[] state = new PropertyState[1]; + Object value; + try { + value = getProperty(handleMap[i], state); + } catch (UnknownPropertyException e) { + continue; + } catch (WrappedTargetException e) { + throw new WrappedTargetRuntimeException(e.getCause(), + e.getMessage(), object, e.TargetException); + } + s[n++] = new PropertyValue(handleMap[i], i, value, state[0]); + } + if (n < handleMap.length) { + PropertyValue[] s2 = new PropertyValue[n]; + System.arraycopy(s, 0, s2, 0, n); + s = s2; + } + return s; + } + + /** + Implements + <code>com.sun.star.beans.XPropertyAccess.setPropertyValues</code>. + + @param props + See com.sun.star.beans.XPropertyAccess + @throws UnknownPropertyException + See com.sun.star.beans.XPropertyAccess + @throws PropertyVetoException + See com.sun.star.beans.XPropertyAccess + @throws WrappedTargetException + See com.sun.star.beans.XPropertyAccess + */ + public void setPropertyValues(PropertyValue[] props) + throws UnknownPropertyException, PropertyVetoException, + com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + for (int i = 0; i < props.length; ++i) { + if (props[i].Handle != -1 + && !props[i].Name.equals(translateHandle(props[i].Handle))) + { + throw new UnknownPropertyException( + ("name " + props[i].Name + " does not match handle " + + props[i].Handle), + object); + } + setProperty( + props[i].Name, props[i].Value, + props[i].State == PropertyState.AMBIGUOUS_VALUE, + props[i].State == PropertyState.DEFAULT_VALUE, (short) 0); + } + } + + /** + A class used by clients of {@link PropertySetMixin} when implementing UNO + interface type attribute setter functions. + + @see #prepareSet(String, Object, Object, PropertySetMixin.BoundListeners) + */ + public static final class BoundListeners { + + /** + Notifies any + <code>com.sun.star.beans.XPropertyChangeListener</code>s. + + @see #prepareSet(String, Object, Object, + PropertySetMixin.BoundListeners) + */ + public void notifyListeners() { + if (specificListeners != null) { + for (Iterator<XPropertyChangeListener> i = specificListeners.iterator(); i.hasNext();) { + try { + i.next().propertyChange(event); + } catch (DisposedException e) {} + } + } + if (unspecificListeners != null) { + for (Iterator<XPropertyChangeListener> i = unspecificListeners.iterator(); i.hasNext();) + { + try { + i.next().propertyChange(event); + } catch (DisposedException e) {} + } + } + } + + private ArrayList<XPropertyChangeListener> specificListeners = null; + private ArrayList<XPropertyChangeListener> unspecificListeners = null; + private PropertyChangeEvent event = null; + } + + private XIdlClass getReflection(String typeName) { + return theCoreReflection.get(context).forName(typeName); + } + + private void initProperties( + XTypeDescription type, HashMap<String,PropertyData> map, ArrayList<String> handleNames, HashSet<String> seen) + { + XInterfaceTypeDescription2 ifc = UnoRuntime.queryInterface( + XInterfaceTypeDescription2.class, resolveTypedefs(type)); + if (!seen.add(ifc.getName())) { + return; + } + XTypeDescription[] bases = ifc.getBaseTypes(); + for (int i = 0; i < bases.length; ++i) { + initProperties(bases[i], map, handleNames, seen); + } + XInterfaceMemberTypeDescription[] members = ifc.getMembers(); + for (int i = 0; i < members.length; ++i) { + if (members[i].getTypeClass() == TypeClass.INTERFACE_ATTRIBUTE) + { + XInterfaceAttributeTypeDescription2 attr = + UnoRuntime.queryInterface( + XInterfaceAttributeTypeDescription2.class, + members[i]); + short attrAttribs = 0; + if (attr.isBound()) { + attrAttribs |= PropertyAttribute.BOUND; + } + boolean setUnknown = false; + if (attr.isReadOnly()) { + attrAttribs |= PropertyAttribute.READONLY; + setUnknown = true; + } + XCompoundTypeDescription[] excs = attr.getGetExceptions(); + boolean getUnknown = false; + //XXX Special interpretation of getter/setter exceptions + // only works if the specified exceptions are of the exact + // type, not of a supertype: + for (int j = 0; j < excs.length; ++j) { + if (excs[j].getName().equals( + "com.sun.star.beans.UnknownPropertyException")) + { + getUnknown = true; + break; + } + } + excs = attr.getSetExceptions(); + for (int j = 0; j < excs.length; ++j) { + if (excs[j].getName().equals( + "com.sun.star.beans.UnknownPropertyException")) + { + setUnknown = true; + } else if (excs[j].getName().equals( + "com.sun.star.beans." + + "PropertyVetoException")) + { + attrAttribs |= PropertyAttribute.CONSTRAINED; + } + } + if (getUnknown && setUnknown) { + attrAttribs |= PropertyAttribute.OPTIONAL; + } + XTypeDescription t = attr.getType(); + for (;;) { + t = resolveTypedefs(t); + short n; + if (t.getName().startsWith( + "com.sun.star.beans.Ambiguous<")) + { + n = PropertyAttribute.MAYBEAMBIGUOUS; + } else if (t.getName().startsWith( + "com.sun.star.beans.Defaulted<")) + { + n = PropertyAttribute.MAYBEDEFAULT; + } else if (t.getName().startsWith( + "com.sun.star.beans.Optional<")) + { + n = PropertyAttribute.MAYBEVOID; + } else { + break; + } + attrAttribs |= n; + t = UnoRuntime.queryInterface(XStructTypeDescription.class, t).getTypeArguments()[0]; + } + String name = members[i].getMemberName(); + boolean present = true; + if (absentOptional != null) { + for (int j = 0; j < absentOptional.length; ++j) { + if (name.equals(absentOptional[j])) { + present = false; + break; + } + } + } + if (map.put( + name, + new PropertyData( + new Property( + name, handleNames.size(), + new Type(t.getName(), t.getTypeClass()), + attrAttribs), + present)) + != null) + { + throw new RuntimeException( + "inconsistent UNO type registry"); + } + handleNames.add(name); + } + } + } + + private String translateHandle(int handle) throws UnknownPropertyException { + if (handle < 0 || handle >= handleMap.length) { + throw new UnknownPropertyException("bad handle " + handle, object); + } + return handleMap[handle]; + } + + private void setProperty( + String name, Object value, boolean isAmbiguous, boolean isDefaulted, + short illegalArgumentPosition) + throws UnknownPropertyException, PropertyVetoException, + com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + PropertyData p = properties.get(name); + if (p == null) { + throw new UnknownPropertyException(name, object); + } + if ((isAmbiguous + && (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) == 0) + || (isDefaulted + && ((p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) + == 0))) + { + throw new com.sun.star.lang.IllegalArgumentException( + ("flagging as ambiguous/defaulted non-ambiguous/defaulted" + + " property " + name), + object, illegalArgumentPosition); + + } + XIdlField2 f = UnoRuntime.queryInterface( + XIdlField2.class, idlClass.getField(name)); + Object[] o = new Object[] { + new Any(type, UnoRuntime.queryInterface(type, object)) }; + Object v = wrapValue( + value, + UnoRuntime.queryInterface( + XIdlField2.class, idlClass.getField(name)).getType(), + (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) != 0, + isAmbiguous, + (p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) != 0, + isDefaulted, + (p.property.Attributes & PropertyAttribute.MAYBEVOID) != 0); + try { + f.set(o, v); + } catch (com.sun.star.lang.IllegalArgumentException e) { + if (e.ArgumentPosition == 1) { + throw new com.sun.star.lang.IllegalArgumentException(e, + e.getMessage(), object, illegalArgumentPosition); + } else { + throw new RuntimeException(e); + } + } catch (com.sun.star.lang.IllegalAccessException e) { + //TODO Clarify whether PropertyVetoException is the correct + // exception to throw when trying to set a read-only property: + throw new PropertyVetoException(e, + "cannot set read-only property " + name, object); + } catch (WrappedTargetRuntimeException e) { + //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not + // guaranteed to originate directly within XIdlField2.get (and thus + // have the expected semantics); it might also be passed through + // from lower layers. + if (new Type(UnknownPropertyException.class).isSupertypeOf( + AnyConverter.getType(e.TargetException)) + && (p.property.Attributes & PropertyAttribute.OPTIONAL) != 0) + { + throw new UnknownPropertyException(e, name, object); + } else if (new Type(PropertyVetoException.class).isSupertypeOf( + AnyConverter.getType(e.TargetException)) + && ((p.property.Attributes + & PropertyAttribute.CONSTRAINED) + != 0)) + { + throw new PropertyVetoException(e, name, object); + } else { + throw new WrappedTargetException(e.getCause(), + e.getMessage(), object, e.TargetException); + } + } + } + + Object getProperty(String name, PropertyState[] state) + throws UnknownPropertyException, WrappedTargetException + { + PropertyData p = properties.get(name); + if (p == null) { + throw new UnknownPropertyException(name, object); + } + XIdlField2 field = UnoRuntime.queryInterface( + XIdlField2.class, idlClass.getField(name)); + Object value; + try { + value = field.get( + new Any(type, UnoRuntime.queryInterface(type, object))); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (WrappedTargetRuntimeException e) { + //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not + // guaranteed to originate directly within XIdlField2.get (and thus + // have the expected semantics); it might also be passed through + // from lower layers. + if (new Type(UnknownPropertyException.class).isSupertypeOf( + AnyConverter.getType(e.TargetException)) + && (p.property.Attributes & PropertyAttribute.OPTIONAL) != 0) + { + throw new UnknownPropertyException(e, name, object); + } else { + throw new WrappedTargetException(e.getCause(), + e.getMessage(), object, e.TargetException); + } + } + boolean undoAmbiguous + = (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) != 0; + boolean undoDefaulted + = (p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) != 0; + boolean undoOptional + = (p.property.Attributes & PropertyAttribute.MAYBEVOID) != 0; + boolean isAmbiguous = false; + boolean isDefaulted = false; + while (undoAmbiguous || undoDefaulted || undoOptional) { + String typeName = AnyConverter.getType(value).getTypeName(); + if (undoAmbiguous + && typeName.startsWith("com.sun.star.beans.Ambiguous<")) + { + XIdlClass ambiguous = getReflection(typeName); + try { + isAmbiguous = AnyConverter.toBoolean( + UnoRuntime.queryInterface( + XIdlField2.class, + ambiguous.getField("IsAmbiguous")).get(value)); + value = UnoRuntime.queryInterface( + XIdlField2.class, + ambiguous.getField("Value")).get(value); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } + undoAmbiguous = false; + } else if (undoDefaulted + && typeName.startsWith("com.sun.star.beans.Defaulted<")) + { + XIdlClass defaulted = getReflection(typeName); + try { + isDefaulted = AnyConverter.toBoolean( + UnoRuntime.queryInterface( + XIdlField2.class, + defaulted.getField("IsDefaulted")).get(value)); + value = UnoRuntime.queryInterface( + XIdlField2.class, + defaulted.getField("Value")).get(value); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } + undoDefaulted = false; + } else if (undoOptional + && typeName.startsWith("com.sun.star.beans.Optional<")) + { + XIdlClass optional = getReflection(typeName); + try { + boolean present = AnyConverter.toBoolean( + UnoRuntime.queryInterface( + XIdlField2.class, + optional.getField("IsPresent")).get(value)); + if (!present) { + value = Any.VOID; + break; + } + value = UnoRuntime.queryInterface( + XIdlField2.class, + optional.getField("Value")).get(value); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } + undoOptional = false; + } else { + throw new RuntimeException( + "unexpected type of attribute " + name); + } + } + if (state != null) { + //XXX If isAmbiguous && isDefaulted, arbitrarily choose + // AMBIGUOUS_VALUE over DEFAULT_VALUE: + state[0] = isAmbiguous + ? PropertyState.AMBIGUOUS_VALUE + : isDefaulted + ? PropertyState.DEFAULT_VALUE : PropertyState.DIRECT_VALUE; + } + return value; + } + + private Object wrapValue( + Object value, XIdlClass type, boolean wrapAmbiguous, + boolean isAmbiguous, boolean wrapDefaulted, boolean isDefaulted, + boolean wrapOptional) + { + if (wrapAmbiguous + && type.getName().startsWith("com.sun.star.beans.Ambiguous<")) + { + Object[] strct = new Object[1]; + type.createObject(strct); + try { + XIdlField2 field = UnoRuntime.queryInterface( + XIdlField2.class, type.getField("Value")); + field.set( + strct, + wrapValue( + value, field.getType(), false, false, wrapDefaulted, + isDefaulted, wrapOptional)); + UnoRuntime.queryInterface( + XIdlField2.class, type.getField("IsAmbiguous")).set( + strct, Boolean.valueOf(isAmbiguous)); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (com.sun.star.lang.IllegalAccessException e) { + throw new RuntimeException(e); + } + return strct[0]; + } else if (wrapDefaulted + && type.getName().startsWith( + "com.sun.star.beans.Defaulted<")) + { + Object[] strct = new Object[1]; + type.createObject(strct); + try { + XIdlField2 field = UnoRuntime.queryInterface( + XIdlField2.class, type.getField("Value")); + field.set( + strct, + wrapValue( + value, field.getType(), wrapAmbiguous, isAmbiguous, + false, false, wrapOptional)); + UnoRuntime.queryInterface( + XIdlField2.class, type.getField("IsDefaulted")).set( + strct, Boolean.valueOf(isDefaulted)); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (com.sun.star.lang.IllegalAccessException e) { + throw new RuntimeException(e); + } + return strct[0]; + } else if (wrapOptional + && type.getName().startsWith("com.sun.star.beans.Optional<")) + { + Object[] strct = new Object[1]; + type.createObject(strct); + boolean present = !AnyConverter.isVoid(value); + try { + UnoRuntime.queryInterface( + XIdlField2.class, type.getField("IsPresent")).set( + strct, Boolean.valueOf(present)); + if (present) { + XIdlField2 field = UnoRuntime.queryInterface( + XIdlField2.class, type.getField("Value")); + field.set( + strct, + wrapValue( + value, field.getType(), wrapAmbiguous, isAmbiguous, + wrapDefaulted, isDefaulted, false)); + } + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (com.sun.star.lang.IllegalAccessException e) { + throw new RuntimeException(e); + } + return strct[0]; + } else { + if (wrapAmbiguous || wrapDefaulted || wrapOptional) { + throw new RuntimeException("unexpected type of attribute"); + } + return value; + } + } + + private static XTypeDescription resolveTypedefs(XTypeDescription type) { + while (type.getTypeClass() == TypeClass.TYPEDEF) { + type = UnoRuntime.queryInterface( + XIndirectTypeDescription.class, type).getReferencedType(); + } + return type; + } + + private PropertyData get(Object object, String propertyName) + throws UnknownPropertyException + { + PropertyData p = properties.get(propertyName); + if (p == null || !p.present) { + throw new UnknownPropertyException(propertyName, object); + } + return p; + } + + private void checkUnknown(String propertyName) + throws UnknownPropertyException + { + if (propertyName.length() != 0) { + get(this, propertyName); + } + } + + private static final class PropertyData { + public PropertyData(Property property, boolean present) { + this.property = property; + this.present = present; + } + + public final Property property; + public final boolean present; + } + + private final class Info extends WeakBase implements XPropertySetInfo + { + public Info(Map<String,PropertyData> properties) { + this.properties = properties; + } + + public Property[] getProperties() { + ArrayList<Property> al = new ArrayList<Property>(properties.size()); + for (Iterator<PropertyData> i = properties.values().iterator(); i.hasNext();) { + PropertyData p = i.next(); + if (p.present) { + al.add(p.property); + } + } + return al.toArray(new Property[al.size()]); + } + + public Property getPropertyByName(String name) + throws UnknownPropertyException + { + return get(this, name).property; + } + + public boolean hasPropertyByName(String name) { + PropertyData p = properties.get(name); + return p != null && p.present; + } + + private final Map<String,PropertyData> properties; + } + + private final XComponentContext context; + private final XInterface object; + private final Type type; + private final String[] absentOptional; + private final XIdlClass idlClass; + private final Map<String,PropertyData> properties; // from String to Property + private final String[] handleMap; + + private HashMap<String,ArrayList<XPropertyChangeListener>> boundListeners + = new HashMap<String,ArrayList<XPropertyChangeListener>>(); + // from String to Vector of XPropertyChangeListener + private HashMap<String,ArrayList<XVetoableChangeListener>> vetoListeners + = new HashMap<String,ArrayList<XVetoableChangeListener>>(); + // from String to Vector of XVetoableChangeListener + private boolean disposed = false; +} diff --git a/ridljar/com/sun/star/lib/uno/helper/UnoUrl.java b/ridljar/com/sun/star/lib/uno/helper/UnoUrl.java new file mode 100644 index 000000000..8bb4d2643 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/UnoUrl.java @@ -0,0 +1,401 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.helper; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.HashMap; + +/** + * Object representation and parsing of Uno Urls, + * which allow to locate a named Uno object in a + * different process. A Uno Url consists of the + * specification of a connection, protocol and + * rootOid delimited with a ';'. + * The syntax of a Uno Url is + * + * <code> + * [uno:]connection-type,parameters;protocol-name,parameters;objectname"; + * </code> + * + * An example Uno Url will look like this: + * + * <code> + * socket,host=localhost,port=2002;urp;StarOffice.ServiceManager + * </code> + * + * For more information about Uno Url please consult + * <a href="http://udk.openoffice.org/common/man/spec/uno-url.html"> + * http://udk.openoffice.org/common/man/spec/uno-url.html</a> + * + * Usage: + * + * <code> + * UnoUrl url = UnoUrl.parseUnoUrl("socket,host=localhost,port=2002;urp;StarOffice.ServiceManager"); + * </code> + */ +public class UnoUrl { + + private static final String FORMAT_ERROR = + "syntax: [uno:]connection-type,parameters;protocol-name,parameters;objectname"; + + private static final String VALUE_CHAR_SET = "!$&'()*+-./:?@_~"; + private static final String OID_CHAR_SET = VALUE_CHAR_SET + ",="; + + private final UnoUrlPart connection; + private final UnoUrlPart protocol; + private final String rootOid; + + private static class UnoUrlPart { + + private final String partTypeName; + private final HashMap<String,String> partParameters; + private final String uninterpretedParameterString; + + public UnoUrlPart( + String uninterpretedParameterString, + String partTypeName, + HashMap<String,String> partParameters) { + this.uninterpretedParameterString = uninterpretedParameterString; + this.partTypeName = partTypeName; + this.partParameters = partParameters; + } + + public String getPartTypeName() { + return partTypeName; + } + + public HashMap<String,String> getPartParameters() { + return partParameters; + } + + public String getUninterpretedParameterString() { + return uninterpretedParameterString; + } + + public String getUninterpretedString() { + StringBuffer buf = new StringBuffer(partTypeName); + if (uninterpretedParameterString.length() > 0) { + buf.append(','); + buf.append(uninterpretedParameterString); + } + return buf.toString(); + } + } + + private UnoUrl( + UnoUrlPart connectionPart, + UnoUrlPart protocolPart, + String rootOid) { + this.connection = connectionPart; + this.protocol = protocolPart; + this.rootOid = rootOid; + } + + /** + * Returns the name of the connection of this + * Uno Url. Encoded characters are not allowed. + * + * @return The connection name as string. + */ + public String getConnection() { + return connection.getPartTypeName(); + } + + /** + * Returns the name of the protocol of this + * Uno Url. Encoded characters are not allowed. + * + * @return The protocol name as string. + */ + public String getProtocol() { + return protocol.getPartTypeName(); + } + + /** + * Return the object name. Encoded character are + * not allowed. + * + * @return The object name as String. + */ + public String getRootOid() { + return rootOid; + } + + /** + * Returns the protocol parameters as + * a Hashmap with key/value pairs. Encoded + * characters like '%41' are decoded. + * + * @return a HashMap with key/value pairs for protocol parameters. + */ + public HashMap<String,String> getProtocolParameters() { + return protocol.getPartParameters(); + } + + /** + * Returns the connection parameters as + * a Hashmap with key/value pairs. Encoded + * characters like '%41' are decoded. + * + * @return a HashMap with key/value pairs for connection parameters. + */ + public HashMap<String,String> getConnectionParameters() { + return connection.getPartParameters(); + } + + /** + * Returns the raw specification of the protocol + * parameters. Encoded characters like '%41' are + * not decoded. + * + * @return The uninterpreted protocol parameters as string. + */ + public String getProtocolParametersAsString() { + return protocol.getUninterpretedParameterString(); + } + + /** + * Returns the raw specification of the connection + * parameters. Encoded characters like '%41' are + * not decoded. + * + * @return The uninterpreted connection parameters as string. + */ + public String getConnectionParametersAsString() { + return connection.getUninterpretedParameterString(); + } + + /** + * Returns the raw specification of the protocol + * name and parameters. Encoded characters like '%41' are + * not decoded. + * + * @return The uninterpreted protocol name and parameters as string. + */ + public String getProtocolAndParametersAsString() { + return protocol.getUninterpretedString(); + } + + /** + * Returns the raw specification of the connection + * name and parameters. Encoded characters like '%41' are + * not decoded. + * + * @return The uninterpreted connection name and parameters as string. + */ + public String getConnectionAndParametersAsString() { + return connection.getUninterpretedString(); + } + + private static String decodeUTF8(String s) + throws com.sun.star.lang.IllegalArgumentException { + + if (!s.contains("%")) { + return s; + } + try { + int length = s.length(); + ByteBuffer bb = ByteBuffer.allocate(length); + for (int i = 0; i < length; i++) { + int ch = s.charAt(i); + + if (ch == '%') { + if (i+3 > length) + throw new com.sun.star.lang.IllegalArgumentException( + "Incomplete trailing escape (%) pattern"); + try { + ch = Integer.parseInt(s.substring(i+1,i+3),16); + } catch (NumberFormatException e) { + throw new com.sun.star.lang.IllegalArgumentException(e); + } + if (ch < 0) + throw new com.sun.star.lang.IllegalArgumentException( + "Illegal hex characters in escape (%) pattern - negative value"); + i+=2; + } + + bb.put((byte) (ch & 0xFF)); + } + + byte[] bytes = new byte[bb.position()]; + System.arraycopy(bb.array(), 0, bytes, 0, bytes.length); + return new String(bytes, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new com.sun.star.lang.IllegalArgumentException(e, + "Couldn't convert parameter string to UTF-8 string"); + } + } + + private static HashMap<String,String> buildParamHashMap(String paramString) + throws com.sun.star.lang.IllegalArgumentException { + HashMap<String,String> params = new HashMap<String,String>(); + + int pos = 0; + + while (true) { + char c = ','; + + StringBuffer sb = new StringBuffer(); + while ((pos < paramString.length()) + && ((c = paramString.charAt(pos++)) != '=')) { + sb.append(c); + } + String aKey = sb.toString(); + + sb = new StringBuffer(); + while ((pos < paramString.length()) + && ((c = paramString.charAt(pos++)) != ',') + && c != ';') { + sb.append(c); + } + String aValue = sb.toString(); + + if ((aKey.length() > 0) && (aValue.length() > 0)) { + + if (!isAlphaNumeric(aKey)) { + throw new com.sun.star.lang.IllegalArgumentException( + "The parameter key '" + + aKey + + "' may only consist of alpha numeric ASCII characters."); + } + + if (!isValidString(aValue, VALUE_CHAR_SET + "%")) { + throw new com.sun.star.lang.IllegalArgumentException( + "The parameter value for key '" + aKey + "' contains illegal characters."); + } + + params.put(aKey, decodeUTF8(aValue)); + } + + if ((pos >= paramString.length()) || (c != ',')) + break; + + } + + return params; + } + + private static UnoUrlPart parseUnoUrlPart(String thePart) + throws com.sun.star.lang.IllegalArgumentException { + String partName; + String theParamPart; + int index = thePart.indexOf(','); + if (index != -1) { + partName = thePart.substring(0, index).trim(); + theParamPart = thePart.substring(index + 1).trim(); + } else { + partName = thePart; + theParamPart = ""; + } + + if (!isAlphaNumeric(partName)) { + throw new com.sun.star.lang.IllegalArgumentException( + "The part name '" + + partName + + "' may only consist of alpha numeric ASCII characters."); + } + + HashMap<String,String> params = buildParamHashMap(theParamPart); + + return new UnoUrlPart(theParamPart, partName, params); + } + + private static boolean isAlphaNumeric(String s) { + return isValidString(s, null); + } + + private static boolean isValidString(String identifier, String validCharSet) { + + int len = identifier.length(); + + for (int i = 0; i < len; i++) { + + int ch = identifier.charAt(i); + + boolean isValidChar = + ('A' <= ch && ch <= 'Z') + || ('a' <= ch && ch <= 'z') + || ('0' <= ch && ch <= '9'); + + if (!isValidChar && (validCharSet != null)) { + isValidChar = (validCharSet.indexOf(ch) != -1); + } + + if (!isValidChar) + return false; + } + + return true; + } + + /** + * Parses the given Uno Url and returns + * an in memory object representation. + * + * @param unoUrl The given uno URl as string. + * @return Object representation of class UnoUrl. + * @throws IllegalArgumentException if Url cannot be parsed. + */ + public static UnoUrl parseUnoUrl(String unoUrl) + throws com.sun.star.lang.IllegalArgumentException { + + String url = unoUrl; + + int index = url.indexOf(':'); + if (index != -1) { + String unoStr = url.substring(0, index).trim(); + if (!"uno".equals(unoStr)) { + throw new com.sun.star.lang.IllegalArgumentException( + "Uno Urls must start with 'uno:'. " + FORMAT_ERROR); + } + } + + url = url.substring(index + 1).trim(); + + index = url.indexOf(';'); + if (index == -1) { + throw new com.sun.star.lang.IllegalArgumentException("'"+unoUrl+"' is an invalid Uno Url. " + FORMAT_ERROR); + } + + String connection = url.substring(0, index).trim(); + url = url.substring(index + 1).trim(); + + UnoUrlPart connectionPart = parseUnoUrlPart(connection); + + index = url.indexOf(';'); + if (index == -1) { + throw new com.sun.star.lang.IllegalArgumentException("'"+unoUrl+"' is an invalid Uno Url. " + FORMAT_ERROR); + } + + String protocol = url.substring(0, index).trim(); + url = url.substring(index + 1).trim(); + + UnoUrlPart protocolPart = parseUnoUrlPart(protocol); + + String rootOid = url.trim(); + if (!isValidString(rootOid, OID_CHAR_SET)) { + throw new com.sun.star.lang.IllegalArgumentException( + "Root OID '"+ rootOid + "' contains illegal characters."); + } + + return new UnoUrl(connectionPart, protocolPart, rootOid); + + } + +} diff --git a/ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java b/ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java new file mode 100644 index 000000000..67e01ac32 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java @@ -0,0 +1,94 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.helper; +import java.lang.ref.WeakReference; +import com.sun.star.uno.XAdapter; +import com.sun.star.uno.XReference; +import java.util.List; +import java.util.Collections; +import java.util.LinkedList; + +/** An XAdapter implementation that holds a weak reference (java.lang.ref.WeakReference) + * to an object. Clients can register listener (com.sun.star.lang.XReference) which + * are notified when the object (the one which is kept weak) is being finalized. That + * is, that object is being destroyed because there are not any hard references + * to it. + */ +public class WeakAdapter implements XAdapter +{ + // references the XWeak implementation + private final WeakReference<Object> m_weakRef; + // contains XReference objects registered by addReference + private final List<XReference> m_xreferenceList; + + /** + *@param component the object that is to be held weak + */ + public WeakAdapter(Object component) + { + m_weakRef= new WeakReference<Object>(component); + m_xreferenceList= Collections.synchronizedList( new LinkedList<XReference>()); + } + + /** Called by the XWeak implementation (WeakBase) when it is being finalized. + * It is only being called once. + * The registered XReference listeners are notified. On notification they are + * to unregister themselves. The notification is thread-safe. However, it is possible + * to add a listener during the notification process, which will never receive a + * notification. To prevent this, one would have to synchronize this method with + * the addReference method. But this can result in deadlocks in a multi-threaded + * environment. + */ + void referentDying() + { + //synchronized call + XReference[] references= m_xreferenceList.toArray(new XReference[m_xreferenceList.size()]); + for (int i= references.length; i > 0; i--) + { + references[i-1].dispose(); + } + } + + /** Method of com.sun.star.uno.XAdapter. It is called to obtain a hard reference + * to the object which is kept weak by this instance. + * @return hard reference to the object + */ + public Object queryAdapted() + { + return m_weakRef.get(); + } + + /** Method of com.sun.star.uno.XAdapter. Called by clients to register listener which + * are notified when the weak object is dying. + *@param xReference a listener + */ + public void removeReference(XReference xReference) + { + m_xreferenceList.remove(xReference); + } + + /** Method of com.sun.star.uno.XAdapter. Called by clients to unregister listeners. + *@param xReference listener + */ + public void addReference(XReference xReference) + { + m_xreferenceList.add(xReference); + } +} + diff --git a/ridljar/com/sun/star/lib/uno/helper/WeakBase.java b/ridljar/com/sun/star/lib/uno/helper/WeakBase.java new file mode 100644 index 000000000..ac175d3a6 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/WeakBase.java @@ -0,0 +1,101 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.helper; +import com.sun.star.uno.XWeak; +import com.sun.star.uno.XAdapter; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.uno.Type; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; + + +/** This class can be used as the base class for UNO components. It implements the capability + * to be kept weak (com.sun.star.uno.XWeak) and it implements com.sun.star.lang.XTypeProvider + * which is necessary for using the component with StarBasic. + */ +public class WeakBase implements XWeak, XTypeProvider +{ + // Contains all WeakAdapter which have been created in this class + // They have to be notified when this object dies + private WeakAdapter m_adapter; + + protected static Map<Class<?>,Type[]> _mapTypes = new HashMap<Class<?>,Type[]>(); + + /** Method of XWeak. The returned XAdapter implementation can be used to keep + * a weak reference to this object. + * @return a com.sun.star.uno.XAdapter implementation. + */ + synchronized public XAdapter queryAdapter() + { + if (m_adapter == null) + m_adapter= new WeakAdapter(this); + return m_adapter; + } + + /** Override of Object.finalize. When there are no references to this object anymore + * then the garbage collector calls this method. Thereby causing the adapter object + * to be notified. The adapter, in turn, notifies all listeners (com.sun.star.uno.XReference) + */ + @Override + protected void finalize() throws java.lang.Throwable + { + if (m_adapter != null) + m_adapter.referentDying(); + super.finalize(); + } + + /** Method of XTypeProvider. It returns an array of Type objects which represent + * all implemented UNO interfaces of this object. + * @return Type objects of all implemented interfaces. + */ + public Type[] getTypes() + { + Type[] arTypes= _mapTypes.get( getClass()); + if (arTypes == null) + { + ArrayList<Type> vec= new ArrayList<Type>(); + Class currentClass= getClass(); + do + { + Class interfaces[]= currentClass.getInterfaces(); + for(int i = 0; i < interfaces.length; ++ i) + { + // Test if it is a UNO interface + if (com.sun.star.uno.XInterface.class.isAssignableFrom(interfaces[i])) + vec.add(new Type(interfaces[i])); + } + // get the superclass the currentClass inherits from + currentClass= currentClass.getSuperclass(); + } while (currentClass != null); + + Type types[]= vec.toArray(new Type[vec.size()]); + _mapTypes.put(getClass(), types); + arTypes= types; + } + return arTypes; + } + + /** Obsolete method of XTypeProvider. + */ + public byte[] getImplementationId() + { + return new byte[0]; + } +} diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/Cache.java b/ridljar/com/sun/star/lib/uno/protocols/urp/Cache.java new file mode 100644 index 000000000..26c2d449d --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/protocols/urp/Cache.java @@ -0,0 +1,114 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.protocols.urp; + +import java.util.HashMap; + +/** + * An LRU cache for arbitrary objects. + * + * <p>This class is not synchronized, as any necessary synchronization will already + * take place in the client.</p> + */ +final class Cache { + /** + * Create a cache. + * + * @param size the maximum cache size, must be between 0, inclusive, and + * NOT_CACHED, exclusive. + */ + public Cache(int size) { + maxSize = size; + } + + public int add(boolean[] found, Object content) { + Entry e = map.get(content); + found[0] = e != null; + if (e == null) { + if (map.size() < maxSize) { + // There is still room for a new entry at the front: + e = new Entry(content, map.size(), null, first); + if (first == null) { + last = e; + } else { + first.prev = e; + } + first = e; + } else if (last != null) { + // Take last entry out and recycle as new front: + map.remove(last.content); + e = last; + e.content = content; + if (first != last) { + // Reached only if maxSize > 1: + last = last.prev; + last.next = null; + e.prev = null; + e.next = first; + first.prev = e; + first = e; + } + } else { + // Reached iff maxSize == 0: + return NOT_CACHED; + } + map.put(content, e); + } else if (e != first) { + // Move to front (reached only if maxSize > 1): + e.prev.next = e.next; + if (e.next == null) { + last = e.prev; + } else { + e.next.prev = e.prev; + } + e.prev = null; + e.next = first; + first.prev = e; + first = e; + } + return e.index; + } + + public static final int NOT_CACHED = 0xFFFF; + + private static final class Entry { + public Entry(Object content, int index, Entry prev, Entry next) { + this.content = content; + this.index = index; + this.prev = prev; + this.next = next; + } + + public Object content; + public int index; + public Entry prev; + public Entry next; + } + + // first/last form a list of 0 to maxSize entries, most recently used first; + // map contains the same entries; each entry has a unique index in the range + // 0 to maxSize - 1 + private final int maxSize; + private final HashMap<Object, Entry> map = new HashMap<Object, Entry>(); // from Object to Entry + private Entry first = null; + private Entry last = null; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/Marshal.java b/ridljar/com/sun/star/lib/uno/protocols/urp/Marshal.java new file mode 100644 index 000000000..106a8736c --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/protocols/urp/Marshal.java @@ -0,0 +1,355 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.protocols.urp; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; + +import com.sun.star.lib.uno.environments.remote.ThreadId; +import com.sun.star.lib.uno.typedesc.FieldDescription; +import com.sun.star.lib.uno.typedesc.TypeDescription; +import com.sun.star.uno.Any; +import com.sun.star.uno.Enum; +import com.sun.star.uno.IBridge; +import com.sun.star.uno.Type; +import com.sun.star.uno.TypeClass; +import com.sun.star.uno.XInterface; + +final class Marshal { + public Marshal(IBridge bridge, short cacheSize) { + this.bridge = bridge; + objectIdCache = new Cache(cacheSize); + threadIdCache = new Cache(cacheSize); + typeCache = new Cache(cacheSize); + } + + public void write8Bit(int value) { + try { + output.writeByte(value); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void write16Bit(int value) { + try { + output.writeShort(value); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void writeObjectId(String objectId) { + try { + if (objectId == null) { + writeStringValue(null); + write16Bit(0xFFFF); + } else { + boolean[] found = new boolean[1]; + int index = objectIdCache.add(found, objectId); + writeStringValue(found[0] ? null : objectId); + write16Bit(index); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void writeInterface(XInterface object, Type type) { + writeObjectId((String) bridge.mapInterfaceTo(object, type)); + } + + public void writeThreadId(ThreadId threadId) { + try { + byte[] data = threadId.getBytes(); + boolean[] found = new boolean[1]; + int index = threadIdCache.add(found, data); + if (found[0]) { + writeCompressedNumber(0); + } else { + writeCompressedNumber(data.length); + writeBytes(data); + } + write16Bit(index); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void writeType(TypeDescription type) { + try { + TypeClass typeClass = type.getTypeClass(); + if (TypeDescription.isTypeClassSimple(typeClass)) { + write8Bit(typeClass.getValue()); + } else { + boolean[] found = new boolean[1]; + int index = typeCache.add(found, type.getTypeName()); + write8Bit(typeClass.getValue() | (found[0] ? 0 : 0x80)); + write16Bit(index); + if (!found[0]) { + writeStringValue(type.getTypeName()); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void writeValue(TypeDescription type, Object value) { + try { + switch(type.getTypeClass().getValue()) { + case TypeClass.VOID_value: + break; + + case TypeClass.BOOLEAN_value: + writeBooleanValue((Boolean) value); + break; + + case TypeClass.BYTE_value: + writeByteValue((Byte) value); + break; + + case TypeClass.SHORT_value: + case TypeClass.UNSIGNED_SHORT_value: + writeShortValue((Short) value); + break; + + case TypeClass.LONG_value: + case TypeClass.UNSIGNED_LONG_value: + writeLongValue((Integer) value); + break; + + case TypeClass.HYPER_value: + case TypeClass.UNSIGNED_HYPER_value: + writeHyperValue((Long) value); + break; + + case TypeClass.FLOAT_value: + writeFloatValue((Float) value); + break; + + case TypeClass.DOUBLE_value: + writeDoubleValue((Double) value); + break; + + case TypeClass.CHAR_value: + writeCharValue((Character) value); + break; + + case TypeClass.STRING_value: + writeStringValue((String) value); + break; + + case TypeClass.TYPE_value: + writeTypeValue((Type) value); + break; + + case TypeClass.ANY_value: + writeAnyValue(value); + break; + + case TypeClass.SEQUENCE_value: + writeSequenceValue(type, value); + break; + + case TypeClass.ENUM_value: + writeEnumValue(type, (Enum) value); + break; + + case TypeClass.STRUCT_value: + writeStructValue(type, value); + break; + + case TypeClass.EXCEPTION_value: + writeExceptionValue(type, (Exception) value); + break; + + case TypeClass.INTERFACE_value: + writeInterfaceValue(type, (XInterface) value); + break; + + default: + throw new IllegalArgumentException("Bad type descriptor " + type); + } + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public byte[] reset() { + byte[] data = buffer.toByteArray(); + buffer.reset(); + return data; + } + + private void writeBooleanValue(Boolean value) throws IOException { + output.writeBoolean(value != null && value.booleanValue()); + } + + private void writeByteValue(Byte value) { + write8Bit(value == null ? 0 : value.byteValue()); + } + + private void writeShortValue(Short value) { + write16Bit(value == null ? 0 : value.shortValue()); + } + + private void writeLongValue(Integer value) throws IOException { + write32Bit(value == null ? 0 : value.intValue()); + } + + private void writeHyperValue(Long value) throws IOException { + output.writeLong(value == null ? 0 : value.longValue()); + } + + private void writeFloatValue(Float value) throws IOException { + output.writeFloat(value == null ? 0 : value.floatValue()); + } + + private void writeDoubleValue(Double value) throws IOException { + output.writeDouble(value == null ? 0 : value.doubleValue()); + } + + private void writeCharValue(Character value) throws IOException { + output.writeChar(value == null ? 0 : value.charValue()); + } + + private void writeStringValue(String value) throws IOException { + if (value == null) { + writeCompressedNumber(0); + } else { + byte[] data = value.getBytes("UTF8"); + writeCompressedNumber(data.length); + writeBytes(data); + } + } + + private void writeTypeValue(Type value) throws ClassNotFoundException { + writeType( + TypeDescription.getTypeDescription( + value == null ? Type.VOID : value)); + } + + private void writeAnyValue(Object value) throws ClassNotFoundException { + TypeDescription type; + if (value == null || value instanceof XInterface) { + type = TypeDescription.getTypeDescription(XInterface.class); + } else if (value instanceof Any) { + Any any = (Any) value; + type = TypeDescription.getTypeDescription(any.getType()); + value = any.getObject(); + } else if (value.getClass() == Object.class) { + // Avoid StackOverflowError: + throw new IllegalArgumentException( + "Object instance does not represent UNO value"); + } else { + type = TypeDescription.getTypeDescription(value.getClass()); + } + writeType(type); + writeValue(type, value); + } + + private void writeSequenceValue(TypeDescription type, Object value) throws IOException { + if (value == null) { + writeCompressedNumber(0); + } else { + TypeDescription ctype = type.getComponentType(); + if (ctype.getTypeClass() == TypeClass.BYTE) { + byte[] data = (byte[]) value; + writeCompressedNumber(data.length); + writeBytes(data); + } else { + int len = Array.getLength(value); + writeCompressedNumber(len); + for (int i = 0; i < len; ++i) { + writeValue(ctype, Array.get(value, i)); + } + } + } + } + + private void writeEnumValue(TypeDescription type, Enum value) throws IllegalAccessException, IOException, InvocationTargetException, NoSuchMethodException { + int n; + if (value == null) { + n = ((Enum) + (type.getZClass().getMethod("getDefault", (Class[]) null). + invoke(null, (Object[]) null))). + getValue(); + } else { + n = value.getValue(); + } + write32Bit(n); + } + + private void writeStructValue(TypeDescription type, Object value) throws IllegalAccessException { + FieldDescription[] fields = type.getFieldDescriptions(); + for (int i = 0; i < fields.length; ++i) { + writeValue( + fields[i].getTypeDescription(), + value == null ? null : fields[i].getField().get(value)); + } + } + + private void writeExceptionValue(TypeDescription type, Exception value) throws IllegalAccessException, IOException { + writeStringValue(value == null ? null : value.getMessage()); + writeStructValue(type, value); + } + + private void writeInterfaceValue(TypeDescription type, XInterface value) { + writeInterface(value, new Type(type)); + } + + private void write32Bit(int value) throws IOException { + output.writeInt(value); + } + + private void writeCompressedNumber(int number) throws IOException { + if (number >= 0 && number < 0xFF) { + write8Bit(number); + } else { + write8Bit(0xFF); + write32Bit(number); + } + } + + private void writeBytes(byte[] data) throws IOException { + output.write(data); + } + + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + private final DataOutput output = new DataOutputStream(buffer); + private final IBridge bridge; + private final Cache objectIdCache; + private final Cache threadIdCache; + private final Cache typeCache; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/PendingRequests.java b/ridljar/com/sun/star/lib/uno/protocols/urp/PendingRequests.java new file mode 100644 index 000000000..928cdcd63 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/protocols/urp/PendingRequests.java @@ -0,0 +1,65 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.protocols.urp; + +import java.util.HashMap; +import java.util.Stack; + +import com.sun.star.lib.uno.environments.remote.ThreadId; +import com.sun.star.lib.uno.typedesc.MethodDescription; + +final class PendingRequests { + + public synchronized void push(ThreadId tid, Item item) { + Stack<Item> s = map.get(tid); + if (s == null) { + s = new Stack<Item>(); + map.put(tid, s); + } + s.push(item); + } + + public synchronized Item pop(ThreadId tid) { + Stack<Item> s = map.get(tid); + Item i = s.pop(); + if (s.empty()) { + map.remove(tid); + } + return i; + } + + public static final class Item { + public Item( + boolean internal, MethodDescription function, Object[] arguments) + { + this.internal = internal; + this.function = function; + this.arguments = arguments; + } + + public final boolean internal; + public final MethodDescription function; + public final Object[] arguments; + } + + private final HashMap<ThreadId, Stack<Item>> map = new HashMap<ThreadId, Stack<Item>>(); // from ThreadId to Stack of Item +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/Unmarshal.java b/ridljar/com/sun/star/lib/uno/protocols/urp/Unmarshal.java new file mode 100644 index 000000000..c16d19291 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/protocols/urp/Unmarshal.java @@ -0,0 +1,476 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.protocols.urp; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; + +import com.sun.star.lib.uno.environments.remote.ThreadId; +import com.sun.star.lib.uno.typedesc.TypeDescription; +import com.sun.star.uno.Any; +import com.sun.star.uno.Enum; +import com.sun.star.uno.IBridge; +import com.sun.star.uno.Type; +import com.sun.star.uno.TypeClass; +import com.sun.star.uno.XInterface; +import com.sun.star.lib.uno.typedesc.FieldDescription; + +final class Unmarshal { + public Unmarshal(IBridge bridge, int cacheSize) { + this.bridge = bridge; + objectIdCache = new String[cacheSize]; + threadIdCache = new ThreadId[cacheSize]; + typeCache = new TypeDescription[cacheSize]; + reset(new byte[0]); + } + + public int read8Bit() { + try { + return input.readUnsignedByte(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public int read16Bit() { + try { + return input.readUnsignedShort(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public String readObjectId() { + try { + String id = readStringValue(); + int index = read16Bit(); + if (index == 0xFFFF) { + if (id.length() == 0) { + id = null; + } + } else { + if (id.length() == 0) { + id = objectIdCache[index]; + } else { + objectIdCache[index] = id; + } + } + return id; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public Object readInterface(Type type) { + String id = readObjectId(); + return id == null ? null : bridge.mapInterfaceFrom(id, type); + } + + public ThreadId readThreadId() { + try { + int len = readCompressedNumber(); + byte[] data ; + ThreadId id = null; + if (len != 0) { + data = new byte[len]; + readBytes(data); + id = new ThreadId(data); + } + int index = read16Bit(); + if (index != 0xFFFF) { + if (len == 0) { + id = threadIdCache[index]; + } else { + threadIdCache[index] = id; + } + } + return id; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public TypeDescription readType() { + int b = read8Bit(); + TypeClass typeClass = TypeClass.fromInt(b & 0x7F); + if (typeClass == null) { + throw new RuntimeException( + "Reading TYPE with bad type class " + (b & 0x7F)); + } + if (TypeDescription.isTypeClassSimple(typeClass)) { + if ((b & 0x80) != 0) { + throw new RuntimeException( + "Reading TYPE with bad type class/cache flag " + b); + } + return TypeDescription.getTypeDescription(typeClass); + } else { + int index = read16Bit(); + TypeDescription type; + if ((b & 0x80) == 0) { + if (index >= typeCache.length) { + throw new RuntimeException( + "Reading TYPE with bad cache index " + index); + } + type = typeCache[index]; + if (type == null) { + throw new RuntimeException( + "Reading TYPE with empty cache index " + index); + } + } else { + try { + type = TypeDescription.getTypeDescription( + readStringValue()); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + if (index != 0xFFFF) { + if (index >= typeCache.length) { + throw new RuntimeException( + "Reading TYPE with bad cache index " + index); + } + typeCache[index] = type; + } + } + return type; + } + } + + public Object readValue(TypeDescription type) { + try { + switch (type.getTypeClass().getValue()) { + case TypeClass.VOID_value: + return null; + + case TypeClass.BOOLEAN_value: + return readBooleanValue(); + + case TypeClass.BYTE_value: + return readByteValue(); + + case TypeClass.SHORT_value: + case TypeClass.UNSIGNED_SHORT_value: + return readShortValue(); + + case TypeClass.LONG_value: + case TypeClass.UNSIGNED_LONG_value: + return readLongValue(); + + case TypeClass.HYPER_value: + case TypeClass.UNSIGNED_HYPER_value: + return readHyperValue(); + + case TypeClass.FLOAT_value: + return readFloatValue(); + + case TypeClass.DOUBLE_value: + return readDoubleValue(); + + case TypeClass.CHAR_value: + return readCharValue(); + + case TypeClass.STRING_value: + return readStringValue(); + + case TypeClass.TYPE_value: + return readTypeValue(); + + case TypeClass.ANY_value: + return readAnyValue(); + + case TypeClass.SEQUENCE_value: + return readSequenceValue(type); + + case TypeClass.ENUM_value: + return readEnumValue(type); + + case TypeClass.STRUCT_value: + return readStructValue(type); + + case TypeClass.EXCEPTION_value: + return readExceptionValue(type); + + case TypeClass.INTERFACE_value: + return readInterfaceValue(type); + + default: + throw new IllegalArgumentException("Bad type descriptor " + type); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public boolean hasMore() { + try { + return input.available() > 0; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void reset(byte[] data) { + input = new DataInputStream(new ByteArrayInputStream(data)); + } + + private Boolean readBooleanValue() throws IOException { + return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE; + } + + private Byte readByteValue() throws IOException { + return Byte.valueOf(input.readByte()); + } + + private Short readShortValue() throws IOException { + return Short.valueOf(input.readShort()); + } + + private Integer readLongValue() throws IOException { + return Integer.valueOf(input.readInt()); + } + + private Long readHyperValue() throws IOException { + return Long.valueOf(input.readLong()); + } + + private Float readFloatValue() throws IOException { + return new Float(input.readFloat()); + } + + private Double readDoubleValue() throws IOException { + return new Double(input.readDouble()); + } + + private Character readCharValue() throws IOException { + return new Character(input.readChar()); + } + + private String readStringValue() throws IOException { + int len = readCompressedNumber(); + byte[] data = new byte[len]; + readBytes(data); + try { + return new String(data, "UTF8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + private Type readTypeValue() { + return new Type(readType()); + } + + private Object readAnyValue() throws IOException { + TypeDescription type = readType(); + switch (type.getTypeClass().getValue()) { + case TypeClass.VOID_value: + return Any.VOID; + + case TypeClass.BOOLEAN_value: + return readBooleanValue(); + + case TypeClass.BYTE_value: + return readByteValue(); + + case TypeClass.SHORT_value: + return readShortValue(); + + case TypeClass.UNSIGNED_SHORT_value: + return new Any(Type.UNSIGNED_SHORT, readShortValue()); + + case TypeClass.LONG_value: + return readLongValue(); + + case TypeClass.UNSIGNED_LONG_value: + return new Any(Type.UNSIGNED_LONG, readLongValue()); + + case TypeClass.HYPER_value: + return readHyperValue(); + + case TypeClass.UNSIGNED_HYPER_value: + return new Any(Type.UNSIGNED_HYPER, readHyperValue()); + + case TypeClass.FLOAT_value: + return readFloatValue(); + + case TypeClass.DOUBLE_value: + return readDoubleValue(); + + case TypeClass.CHAR_value: + return readCharValue(); + + case TypeClass.STRING_value: + return readStringValue(); + + case TypeClass.TYPE_value: + return readTypeValue(); + + case TypeClass.SEQUENCE_value: + { + Object value = readSequenceValue(type); + TypeDescription ctype = type.getComponentType(); + while (ctype.getTypeClass() == TypeClass.SEQUENCE) { + ctype = ctype.getComponentType(); + } + switch (ctype.getTypeClass().getValue()) { + case TypeClass.UNSIGNED_SHORT_value: + case TypeClass.UNSIGNED_LONG_value: + case TypeClass.UNSIGNED_HYPER_value: + return new Any(new Type(type), value); + + case TypeClass.STRUCT_value: + if (ctype.hasTypeArguments()) { + return new Any(new Type(type), value); + } + default: + return value; + } + } + + case TypeClass.ENUM_value: + return readEnumValue(type); + + case TypeClass.STRUCT_value: + { + Object value = readStructValue(type); + return type.hasTypeArguments() + ? new Any(new Type(type), value) : value; + } + + case TypeClass.EXCEPTION_value: + return readExceptionValue(type); + + case TypeClass.INTERFACE_value: + { + Object value = readInterfaceValue(type); + return type.getZClass() == XInterface.class + ? value : new Any(new Type(type), value); + } + + default: + throw new RuntimeException( + "Reading ANY with bad type " + type.getTypeClass()); + } + } + + private Object readSequenceValue(TypeDescription type) throws IOException { + int len = readCompressedNumber(); + TypeDescription ctype = type.getComponentType(); + if (ctype.getTypeClass() == TypeClass.BYTE) { + byte[] data = new byte[len]; + readBytes(data); + return data; + } else { + Object value = Array.newInstance( + ctype.getTypeClass() == TypeClass.ANY + ? Object.class : ctype.getZClass(), len); + for (int i = 0; i < len; ++i) { + Array.set(value, i, readValue(ctype)); + } + return value; + } + } + + private Enum readEnumValue(TypeDescription type) throws IOException { + try { + return (Enum) + type.getZClass().getMethod( + "fromInt", new Class[] { int.class }). + invoke(null, new Object[] { readLongValue() }); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private Object readStructValue(TypeDescription type) { + Object value; + try { + value = type.getZClass().newInstance(); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } + readFields(type, value); + return value; + } + + private Exception readExceptionValue(TypeDescription type) throws IOException { + Exception value; + try { + value = (Exception) + type.getZClass().getConstructor(new Class[] { String.class }). + newInstance(new Object[] { readStringValue() }); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + readFields(type, value); + return value; + } + + private Object readInterfaceValue(TypeDescription type) { + return readInterface(new Type(type)); + } + + private int readCompressedNumber() throws IOException { + int number = read8Bit(); + return number < 0xFF ? number : input.readInt(); + } + + private void readBytes(byte[] data) throws IOException { + input.readFully(data); + } + + private void readFields(TypeDescription type, Object value) { + FieldDescription[] fields = type.getFieldDescriptions(); + for (int i = 0; i < fields.length; ++i) { + try { + fields[i].getField().set( + value, + readValue( + fields[i].getTypeDescription())); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + + private final IBridge bridge; + private final String[] objectIdCache; + private final ThreadId[] threadIdCache; + private final TypeDescription[] typeCache; + private DataInputStream input; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/UrpMessage.java b/ridljar/com/sun/star/lib/uno/protocols/urp/UrpMessage.java new file mode 100644 index 000000000..34cdf7073 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/protocols/urp/UrpMessage.java @@ -0,0 +1,48 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.protocols.urp; + +import com.sun.star.lib.uno.environments.remote.Message; +import com.sun.star.lib.uno.environments.remote.ThreadId; +import com.sun.star.lib.uno.typedesc.MethodDescription; +import com.sun.star.lib.uno.typedesc.TypeDescription; +import com.sun.star.uno.XCurrentContext; + +final class UrpMessage extends Message { + public UrpMessage( + ThreadId threadId, boolean request, String objectId, + TypeDescription type, MethodDescription method, boolean synchronous, + XCurrentContext currentContext, boolean abnormalTermination, + Object result, Object[] arguments, boolean internal) + { + super( + threadId, request, objectId, type, method, synchronous, + currentContext, abnormalTermination, result, arguments); + this.internal = internal; + } + + public boolean isInternal() { + return internal; + } + + private final boolean internal; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/protocols/urp/urp.java b/ridljar/com/sun/star/lib/uno/protocols/urp/urp.java new file mode 100644 index 000000000..0ce9d35be --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/protocols/urp/urp.java @@ -0,0 +1,760 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.protocols.urp; + +import com.sun.star.bridge.InvalidProtocolChangeException; +import com.sun.star.bridge.ProtocolProperty; +import com.sun.star.bridge.XProtocolProperties; +import com.sun.star.lang.DisposedException; +import com.sun.star.lib.uno.environments.remote.IProtocol; +import com.sun.star.lib.uno.environments.remote.Message; +import com.sun.star.lib.uno.environments.remote.ThreadId; +import com.sun.star.lib.uno.typedesc.MethodDescription; +import com.sun.star.lib.uno.typedesc.TypeDescription; +import com.sun.star.uno.Any; +import com.sun.star.uno.IBridge; +import com.sun.star.uno.Type; +import com.sun.star.uno.TypeClass; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XCurrentContext; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Random; +import java.util.StringTokenizer; + +/** + * This class internally relies on the availability of Java UNO type information + * for the interface type <code>com.sun.star.bridge.XProtocolProperties</code>, + * even though URP itself does not rely on that type. + */ +public final class urp implements IProtocol { + public urp( + IBridge bridge, String attributes, InputStream input, + OutputStream output) + { + this.input = new DataInputStream(input); + this.output = new DataOutputStream(output); + marshal = new Marshal(bridge, CACHE_SIZE); + unmarshal = new Unmarshal(bridge, CACHE_SIZE); + forceSynchronous = parseAttributes(attributes); + } + + /** + * + * @see IProtocol#init + */ + public void init() throws IOException { + synchronized (monitor) { + if (state == STATE_INITIAL0) { + sendRequestChange(); + } + } + } + + /** + * + * @see IProtocol#terminate + */ + public void terminate() { + synchronized (monitor) { + state = STATE_TERMINATED; + initialized = true; + monitor.notifyAll(); + } + } + + /** + * + * @see IProtocol#readMessage + */ + public Message readMessage() throws IOException { + for (;;) { + if (!unmarshal.hasMore()) { + unmarshal.reset(readBlock()); + if (!unmarshal.hasMore()) { + throw new IOException("closeConnection message received"); + } + } + UrpMessage msg; + int header = unmarshal.read8Bit(); + if ((header & HEADER_LONGHEADER) != 0) { + if ((header & HEADER_REQUEST) != 0) { + msg = readLongRequest(header); + } else { + msg = readReply(header); + } + } else { + msg = readShortRequest(header); + } + if (msg.isInternal()) { + handleInternalMessage(msg); + } else { + return msg; + } + } + } + + /** + * + * @see IProtocol#writeRequest + */ + public boolean writeRequest( + String oid, TypeDescription type, String function, ThreadId tid, + Object[] arguments) + throws IOException + { + if (oid.equals(PROPERTIES_OID)) { + throw new IllegalArgumentException("illegal OID " + oid); + } + synchronized (monitor) { + while (!initialized) { + try { + monitor.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + if (state == STATE_TERMINATED) { + throw new DisposedException(); + } + return writeRequest(false, oid, type, function, tid, arguments); + } + } + + /** + * + * @see IProtocol#writeReply + */ + public void writeReply(boolean exception, ThreadId tid, Object result) + throws IOException + { + synchronized (output) { + writeQueuedReleases(); + int header = HEADER_LONGHEADER; + PendingRequests.Item pending = pendingIn.pop(tid); + TypeDescription resultType; + TypeDescription[] argTypes; + Object[] args; + if (exception) { + header |= HEADER_EXCEPTION; + resultType = TypeDescription.getTypeDescription(TypeClass.ANY); + argTypes = null; + args = null; + } else { + resultType = pending.function.getReturnSignature(); + argTypes = pending.function.getOutSignature(); + args = pending.arguments; + } + if (!tid.equals(outL1Tid)) { + header |= HEADER_NEWTID; + outL1Tid = tid; + } else { + tid = null; + } + marshal.write8Bit(header); + if (tid != null) { + marshal.writeThreadId(tid); + } + marshal.writeValue(resultType, result); + if (argTypes != null) { + for (int i = 0; i < argTypes.length; ++i) { + if (argTypes[i] != null) { + marshal.writeValue( + argTypes[i].getComponentType(), + Array.get(args[i], 0)); + } + } + } + writeBlock(true); + } + } + + private void sendRequestChange() throws IOException { + if (propertiesTid == null) { + propertiesTid = ThreadId.createFresh(); + } + random = randomGenerator.nextInt(); + writeRequest( + true, PROPERTIES_OID, + TypeDescription.getTypeDescription(XProtocolProperties.class), + PROPERTIES_FUN_REQUEST_CHANGE, propertiesTid, + new Object[] { Integer.valueOf(random) }); + state = STATE_REQUESTED; + } + + private void handleInternalMessage(Message message) throws IOException { + if (message.isRequest()) { + String t = message.getType().getTypeName(); + if (!t.equals("com.sun.star.bridge.XProtocolProperties")) { + throw new IOException( + "read URP protocol properties request with unsupported" + + " type " + t); + } + int fid = message.getMethod().getIndex(); + switch (fid) { + case PROPERTIES_FID_REQUEST_CHANGE: + checkSynchronousPropertyRequest(message); + synchronized (monitor) { + switch (state) { + case STATE_INITIAL0: + case STATE_INITIAL: + writeReply( + false, message.getThreadId(), Integer.valueOf(1)); + state = STATE_WAIT; + break; + case STATE_REQUESTED: + int n + = ((Integer) message.getArguments()[0]).intValue(); + if (random < n) { + writeReply( + false, message.getThreadId(), Integer.valueOf(1)); + state = STATE_WAIT; + } else if (random == n) { + writeReply( + false, message.getThreadId(), Integer.valueOf(-1)); + state = STATE_INITIAL; + sendRequestChange(); + } else { + writeReply( + false, message.getThreadId(), Integer.valueOf(0)); + } + break; + default: + writeReply( + true, message.getThreadId(), + new com.sun.star.uno.RuntimeException( + "read URP protocol properties requestChange" + + " request in illegal state")); + break; + } + } + break; + case PROPERTIES_FID_COMMIT_CHANGE: + checkSynchronousPropertyRequest(message); + synchronized (monitor) { + if (state == STATE_WAIT) { + ProtocolProperty[] p = (ProtocolProperty[]) + message.getArguments()[0]; + boolean ok = true; + boolean cc = false; + int i = 0; + for (; i < p.length; ++i) { + if (p[i].Name.equals(PROPERTY_CURRENT_CONTEXT)) { + cc = true; + } else { + ok = false; + break; + } + } + if (ok) { + writeReply(false, message.getThreadId(), null); + } else { + writeReply( + true, message.getThreadId(), + new InvalidProtocolChangeException( + "", null, p[i], 1)); + } + state = STATE_INITIAL; + if (!initialized) { + if (cc) { + currentContext = true; + initialized = true; + monitor.notifyAll(); + } else { + sendRequestChange(); + } + } + } else { + writeReply( + true, message.getThreadId(), + new com.sun.star.uno.RuntimeException( + "read URP protocol properties commitChange" + + " request in illegal state")); + } + } + break; + default: + throw new IOException( + "read URP protocol properties request with unsupported" + + " function ID " + fid); + } + } else { + synchronized (monitor) { + if (state == STATE_COMMITTED) { + // commitChange reply: + if (!message.isAbnormalTermination()) { + currentContext = true; + } + state = STATE_INITIAL; + initialized = true; + monitor.notifyAll(); + } else { + // requestChange reply: + if (message.isAbnormalTermination()) { + // remote side probably does not support negotiation: + state = STATE_INITIAL; + initialized = true; + monitor.notifyAll(); + } else { + int n = ((Integer) message.getResult()).intValue(); + switch (n) { + case -1: + case 0: + break; + case 1: + writeRequest( + true, PROPERTIES_OID, + TypeDescription.getTypeDescription( + XProtocolProperties.class), + PROPERTIES_FUN_COMMIT_CHANGE, propertiesTid, + new Object[] { + new ProtocolProperty[] { + new ProtocolProperty( + PROPERTY_CURRENT_CONTEXT, + Any.VOID) } }); + state = STATE_COMMITTED; + break; + default: + throw new IOException( + "read URP protocol properties " + + PROPERTIES_FUN_REQUEST_CHANGE + + " reply with illegal return value " + n); + } + } + } + } + } + } + + private void checkSynchronousPropertyRequest(Message message) + throws IOException + { + if (!message.isSynchronous()) { + throw new IOException( + "read URP protocol properties request for synchronous function" + + " marked as not SYNCHRONOUS"); + } + } + + private byte[] readBlock() throws IOException { + int size = input.readInt(); + input.readInt(); // ignore count + byte[] bytes = new byte[size]; + input.readFully(bytes); + return bytes; + } + + private UrpMessage readLongRequest(int header) throws IOException { + boolean sync = false; + if ((header & HEADER_MOREFLAGS) != 0) { + if (unmarshal.read8Bit() != (HEADER_MUSTREPLY | HEADER_SYNCHRONOUS)) + { + throw new IOException( + "read URP request with bad MUSTREPLY/SYNCHRONOUS byte"); + } + sync = true; + } + int funId = (header & HEADER_FUNCTIONID16) != 0 + ? unmarshal.read16Bit() : unmarshal.read8Bit(); + if ((header & HEADER_NEWTYPE) != 0) { + inL1Type = unmarshal.readType(); + if (inL1Type.getTypeClass() != TypeClass.INTERFACE) { + throw new IOException( + "read URP request with non-interface type " + inL1Type); + } + } + if ((header & HEADER_NEWOID) != 0) { + inL1Oid = unmarshal.readObjectId(); + } + if ((header & HEADER_NEWTID) != 0) { + inL1Tid = unmarshal.readThreadId(); + } + return readRequest(funId, sync); + } + + private UrpMessage readShortRequest(int header) throws IOException { + int funId = (header & HEADER_FUNCTIONID14) != 0 + ? ((header & HEADER_FUNCTIONID) << 8) | unmarshal.read8Bit() + : header & HEADER_FUNCTIONID; + return readRequest(funId, false); + } + + private UrpMessage readRequest(int functionId, boolean forcedSynchronous) + throws IOException + { + boolean internal = PROPERTIES_OID.equals(inL1Oid); + // inL1Oid may be null in XInstanceProvider.getInstance("") + XCurrentContext cc = + (currentContext && !internal + && functionId != MethodDescription.ID_RELEASE) + ? (XCurrentContext) unmarshal.readInterface( + new Type(XCurrentContext.class)) + : null; + MethodDescription desc = inL1Type.getMethodDescription(functionId); + if (desc == null) { + throw new IOException( + "read URP request with unsupported function ID " + functionId); + } + TypeDescription[] inSig = desc.getInSignature(); + TypeDescription[] outSig = desc.getOutSignature(); + Object[] args = new Object[inSig.length]; + for (int i = 0; i < args.length; ++i) { + if (inSig[i] != null) { + if (outSig[i] != null) { + Object inout = Array.newInstance( + outSig[i].getComponentType().getZClass(), 1); + Array.set( + inout, 0, + unmarshal.readValue( + outSig[i].getComponentType())); + args[i] = inout; + } else { + args[i] = unmarshal.readValue(inSig[i]); + } + } else { + args[i] = Array.newInstance( + outSig[i].getComponentType().getZClass(), 1); + } + } + boolean sync = forcedSynchronous || !desc.isOneway(); + if (sync) { + pendingIn.push( + inL1Tid, new PendingRequests.Item(internal, desc, args)); + } + return new UrpMessage( + inL1Tid, true, inL1Oid, inL1Type, desc, sync, cc, false, null, args, + internal); + } + + private UrpMessage readReply(int header) { + if ((header & HEADER_NEWTID) != 0) { + inL1Tid = unmarshal.readThreadId(); + } + PendingRequests.Item pending = pendingOut.pop(inL1Tid); + TypeDescription resultType; + TypeDescription[] argTypes; + Object[] args; + boolean exception = (header & HEADER_EXCEPTION) != 0; + if (exception) { + resultType = TypeDescription.getTypeDescription(TypeClass.ANY); + argTypes = null; + args = null; + } else { + resultType = pending.function.getReturnSignature(); + argTypes = pending.function.getOutSignature(); + args = pending.arguments; + } + Object result = resultType == null + ? null : unmarshal.readValue(resultType); + if (argTypes != null) { + for (int i = 0; i < argTypes.length; ++i) { + if (argTypes[i] != null) { + Array.set( + args[i], 0, + unmarshal.readValue( + argTypes[i].getComponentType())); + } + } + } + return new UrpMessage( + inL1Tid, false, null, null, null, false, null, exception, result, + args, pending.internal); + } + + private boolean writeRequest( + boolean internal, String oid, TypeDescription type, String function, + ThreadId tid, Object[] arguments) + throws IOException + { + MethodDescription desc = type.getMethodDescription(function); + synchronized (output) { + if (desc.getIndex() == MethodDescription.ID_RELEASE + && releaseQueue.size() < MAX_RELEASE_QUEUE_SIZE) + { + releaseQueue.add( + new QueuedRelease(internal, oid, type, desc, tid)); + return false; + } else { + writeQueuedReleases(); + return writeRequest( + internal, oid, type, desc, tid, arguments, true); + } + } + } + + private boolean writeRequest( + boolean internal, String oid, TypeDescription type, + MethodDescription desc, ThreadId tid, Object[] arguments, + boolean flush) + throws IOException + { + int funId = desc.getIndex(); + if (funId < 0 || funId > MAX_FUNCTIONID16) { + throw new IllegalArgumentException( + "function ID " + funId + " out of range"); + } + boolean forceSync = forceSynchronous + && funId != MethodDescription.ID_RELEASE; + boolean moreFlags = forceSync && desc.isOneway(); + boolean longHeader = moreFlags; + int header = 0; + if (!type.equals(outL1Type)) { + longHeader = true; + header |= HEADER_NEWTYPE; + outL1Type = type; + } else { + type = null; + } + if (!oid.equals(outL1Oid)) { + longHeader = true; + header |= HEADER_NEWOID; + outL1Oid = oid; + } else { + oid = null; + } + if (!tid.equals(outL1Tid)) { + longHeader = true; + header |= HEADER_NEWTID; + outL1Tid = tid; + } else { + tid = null; + } + if (funId > MAX_FUNCTIONID14) { + longHeader = true; + } + if (longHeader) { + header |= HEADER_LONGHEADER | HEADER_REQUEST; + if (funId > MAX_FUNCTIONID8) { + header |= HEADER_FUNCTIONID16; + } + if (moreFlags) { + header |= HEADER_MOREFLAGS; + } + marshal.write8Bit(header); + if (moreFlags) { + marshal.write8Bit(HEADER_MUSTREPLY | HEADER_SYNCHRONOUS); + } + if (funId > MAX_FUNCTIONID8) { + marshal.write16Bit(funId); + } else { + marshal.write8Bit(funId); + } + if (type != null) { + marshal.writeType(type); + } + if (oid != null) { + marshal.writeObjectId(oid); + } + if (tid != null) { + marshal.writeThreadId(tid); + } + } else { + if (funId > HEADER_FUNCTIONID) { + marshal.write8Bit(HEADER_FUNCTIONID14 | (funId >> 8)); + } + marshal.write8Bit(funId); + } + if (currentContext && !internal + && funId != MethodDescription.ID_RELEASE) + { + marshal.writeInterface( + UnoRuntime.getCurrentContext(), + new Type(XCurrentContext.class)); + } + TypeDescription[] inSig = desc.getInSignature(); + TypeDescription[] outSig = desc.getOutSignature(); + for (int i = 0; i < inSig.length; ++i) { + if (inSig[i] != null) { + if (outSig[i] != null) { + marshal.writeValue( + outSig[i].getComponentType(), + ((Object[]) arguments[i])[0]); + } else { + marshal.writeValue( + inSig[i], arguments[i]); + } + } + } + boolean sync = forceSync || !desc.isOneway(); + if (sync) { + pendingOut.push( + outL1Tid, new PendingRequests.Item(internal, desc, arguments)); + } + writeBlock(flush); + return sync; + } + + private void writeBlock(boolean flush) throws IOException { + byte[] data = marshal.reset(); + output.writeInt(data.length); + output.writeInt(1); + output.write(data); + if (flush) { + output.flush(); + } + } + + private void writeQueuedReleases() throws IOException { + for (int i = releaseQueue.size(); i > 0;) { + --i; + QueuedRelease r = releaseQueue.get(i); + writeRequest( + r.internal, r.objectId, r.type, r.method, r.threadId, null, + false); + releaseQueue.remove(i); + } + } + + private static boolean parseAttributes(String attributes) { + boolean forceSynchronous = true; + if (attributes != null) { + StringTokenizer t = new StringTokenizer(attributes, ","); + while (t.hasMoreTokens()) { + String a = t.nextToken(); + String v = null; + int i = a.indexOf('='); + if (i >= 0) { + v = a.substring(i + 1); + a = a.substring(0, i); + } + if (a.equalsIgnoreCase("ForceSynchronous")) { + forceSynchronous = parseBooleanAttributeValue(a, v); + } else if (a.equalsIgnoreCase("negotiate")) { + // Ignored: + parseBooleanAttributeValue(a, v); + } else { + throw new IllegalArgumentException( + "unknown protocol attribute " + a); + } + } + } + return forceSynchronous; + } + + private static boolean parseBooleanAttributeValue( + String attribute, String value) + { + if (value == null) { + throw new IllegalArgumentException( + "missing value for protocol attribute " + attribute); + } + if (value.equals("0")) { + return false; + } else if (value.equals("1")) { + return true; + } else { + throw new IllegalArgumentException( + "bad value " + value + " for protocol attribute " + attribute); + } + } + + private static final class QueuedRelease { + public QueuedRelease( + boolean internal, String objectId, TypeDescription type, + MethodDescription method, ThreadId threadId) + { + this.internal = internal; + this.objectId = objectId; + this.type = type; + this.method = method; + this.threadId = threadId; + } + + public final boolean internal; + public final String objectId; + public final TypeDescription type; + public final MethodDescription method; + public final ThreadId threadId; + } + + private static final String PROPERTIES_OID = "UrpProtocolProperties"; + private static final int PROPERTIES_FID_REQUEST_CHANGE = 4; + private static final String PROPERTIES_FUN_REQUEST_CHANGE = "requestChange"; + private static final int PROPERTIES_FID_COMMIT_CHANGE = 5; + private static final String PROPERTIES_FUN_COMMIT_CHANGE = "commitChange"; + private static final String PROPERTY_CURRENT_CONTEXT = "CurrentContext"; + + private static final short CACHE_SIZE = 256; + + private static final int HEADER_LONGHEADER = 0x80; + private static final int HEADER_REQUEST = 0x40; + private static final int HEADER_NEWTYPE = 0x20; + private static final int HEADER_NEWOID = 0x10; + private static final int HEADER_NEWTID = 0x08; + private static final int HEADER_FUNCTIONID16 = 0x04; + private static final int HEADER_MOREFLAGS = 0x01; + private static final int HEADER_MUSTREPLY = 0x80; + private static final int HEADER_SYNCHRONOUS = 0x40; + private static final int HEADER_FUNCTIONID14 = 0x40; + private static final int HEADER_FUNCTIONID = 0x3F; + private static final int HEADER_EXCEPTION = 0x20; + + private static final int MAX_FUNCTIONID16 = 0xFFFF; + private static final int MAX_FUNCTIONID14 = 0x3FFF; + private static final int MAX_FUNCTIONID8 = 0xFF; + + private static final int STATE_INITIAL0 = 0; + private static final int STATE_INITIAL = 1; + private static final int STATE_REQUESTED = 2; + private static final int STATE_COMMITTED = 3; + private static final int STATE_WAIT = 4; + private static final int STATE_TERMINATED = 5; + + private static final int MAX_RELEASE_QUEUE_SIZE = 100; + + private static final Random randomGenerator = new Random(); + + private final DataInput input; + private final DataOutputStream output; + + private final Marshal marshal; + private final Unmarshal unmarshal; + + private final boolean forceSynchronous; + + private final PendingRequests pendingIn = new PendingRequests(); + private final PendingRequests pendingOut = new PendingRequests(); + + private final Object monitor = new Object(); + private int state = STATE_INITIAL0; + private boolean initialized = false; + private ThreadId propertiesTid = null; + private int random; + private boolean currentContext = false; + + private ThreadId inL1Tid = null; + private String inL1Oid = null; + private TypeDescription inL1Type = null; + + private ThreadId outL1Tid = null; + private String outL1Oid = null; + private TypeDescription outL1Type = null; + + private final ArrayList<QueuedRelease> releaseQueue = new ArrayList<QueuedRelease>(); // of QueuedRelease +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/uno/typedesc/FieldDescription.java b/ridljar/com/sun/star/lib/uno/typedesc/FieldDescription.java new file mode 100644 index 000000000..6dcdc99db --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/typedesc/FieldDescription.java @@ -0,0 +1,78 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.typedesc; + +import java.lang.reflect.Field; + +/** + * Describes non method members. + */ +public final class FieldDescription { + public FieldDescription( + String name, int index, TypeDescription typeDescription, Field field) + { + this.name = name; + this.index = index; + this.typeDescription = typeDescription; + this.field = field; + } + + public String getName() { + return name; + } + + public boolean isUnsigned() { + return MemberDescriptionHelper.isUnsigned(typeDescription); + } + + public boolean isAny() { + return MemberDescriptionHelper.isAny(typeDescription); + } + + public boolean isInterface() { + return MemberDescriptionHelper.isInterface(typeDescription); + } + + public int getIndex() { + return index; + } + + /** + * Gives the name of this member. + * <p> + * @return the name + */ + public TypeDescription getTypeDescription() { + return typeDescription; + } + + /** + * Gives native java field of this member. + * <p> + * @return the java field + */ + public Field getField() { + return field; + } + + private final String name; + private final int index; + private final TypeDescription typeDescription; + private final Field field; +} diff --git a/ridljar/com/sun/star/lib/uno/typedesc/MemberDescriptionHelper.java b/ridljar/com/sun/star/lib/uno/typedesc/MemberDescriptionHelper.java new file mode 100644 index 000000000..27a5361f3 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/typedesc/MemberDescriptionHelper.java @@ -0,0 +1,54 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.typedesc; + +import com.sun.star.uno.TypeClass; + +final class MemberDescriptionHelper { + public static boolean isUnsigned(TypeDescription desc) { + switch (getElementTypeClass(desc).getValue()) { + case TypeClass.UNSIGNED_SHORT_value: + case TypeClass.UNSIGNED_LONG_value: + case TypeClass.UNSIGNED_HYPER_value: + return true; + + default: + return false; + } + } + + public static boolean isAny(TypeDescription desc) { + return getElementTypeClass(desc) == TypeClass.ANY; + } + + public static boolean isInterface(TypeDescription desc) { + return getElementTypeClass(desc) == TypeClass.INTERFACE; + } + + private static TypeClass getElementTypeClass(TypeDescription desc) { + for (;; desc = desc.getComponentType()) { + TypeClass tc = desc.getTypeClass(); + if (tc != TypeClass.SEQUENCE) { + return tc; + } + } + } + + private MemberDescriptionHelper() {} // do not instantiate +} diff --git a/ridljar/com/sun/star/lib/uno/typedesc/MethodDescription.java b/ridljar/com/sun/star/lib/uno/typedesc/MethodDescription.java new file mode 100644 index 000000000..6cbbb1678 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/typedesc/MethodDescription.java @@ -0,0 +1,131 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.typedesc; + +import java.lang.reflect.Method; + +/** + * Allows to examine a method in detail. It gives a view to java methods from a UNO point. + */ +public final class MethodDescription { + MethodDescription( + String name, int index, boolean oneway, TypeDescription[] inSignature, + TypeDescription[] outSignature, TypeDescription returnSignature, + Method method) + { + this.name = name; + this.index = index; + this.oneway = oneway; + this.inSignature = inSignature; + this.outSignature = outSignature; + this.returnSignature = returnSignature; + this.method = method; + } + + MethodDescription(MethodDescription other, int index) { + this( + other.getName(), index, other.isOneway(), other.getInSignature(), + other.getOutSignature(), other.getReturnSignature(), + other.getMethod()); + } + + public String getName() { + return name; + } + + public boolean isUnsigned() { + return MemberDescriptionHelper.isUnsigned(returnSignature); + } + + public boolean isAny() { + return MemberDescriptionHelper.isAny(returnSignature); + } + + public boolean isInterface() { + return MemberDescriptionHelper.isInterface(returnSignature); + } + + public int getIndex() { + return index; + } + + /** + * Indicates if this method is <code>oneWay</code>, + * respectively if this method may become executed asynchronously. + * @return true means may execute asynchronously . + */ + public boolean isOneway() { + return oneway; + } + + /** + * Indicates if this method is const. + * @return true means it is const. + */ + public boolean isConst() { + return false; + } + + /** + * Gives any array of <code>TypeDescription</code> of + * the [in] parameters. + * @return the in parameters + */ + public TypeDescription[] getInSignature() { + return inSignature; + } + + /** + * Gives any array of <code>TypeDescription</code> of + * the [out] parameters. + * @return the out parameters + */ + public TypeDescription[] getOutSignature() { + return outSignature; + } + + /** + * Gives the <code>TypeDescription</code> of + * the return type. + * @return the return type <code>TypeDescription</code> + */ + public TypeDescription getReturnSignature() { + return returnSignature; + } + + /** + * Gives native java method of this method. + * @return the java method + */ + public Method getMethod() { + return method; + } + + public static final int ID_QUERY_INTERFACE = 0; + public static final int ID_ACQUIRE = 1; + public static final int ID_RELEASE = 2; + + private final String name; + private final int index; + private final boolean oneway; + private final TypeDescription[] inSignature; + private final TypeDescription[] outSignature; + private final TypeDescription returnSignature; + private final Method method; +} diff --git a/ridljar/com/sun/star/lib/uno/typedesc/TypeDescription.java b/ridljar/com/sun/star/lib/uno/typedesc/TypeDescription.java new file mode 100644 index 000000000..a3c3bcd52 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/typedesc/TypeDescription.java @@ -0,0 +1,819 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.uno.typedesc; + +import com.sun.star.lib.uno.typeinfo.AttributeTypeInfo; +import com.sun.star.lib.uno.typeinfo.MemberTypeInfo; +import com.sun.star.lib.uno.typeinfo.MethodTypeInfo; +import com.sun.star.lib.uno.typeinfo.ParameterTypeInfo; +import com.sun.star.lib.uno.typeinfo.TypeInfo; +import com.sun.star.uno.Type; +import com.sun.star.uno.TypeClass; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Supplies information about UNO types. Allows to examine a type + * in detail (e.g. it is used for marshaling/unmarshaling). + * + * @since UDK2.0 + */ +public final class TypeDescription { + public static TypeDescription getTypeDescription(String typeName) + throws ClassNotFoundException + { + Type t = new Type(typeName); + if (t.getTypeClass() == TypeClass.UNKNOWN) { + if (typeName.startsWith("[]")) { + t = new Type(typeName, TypeClass.SEQUENCE); + } else { + t = new Type(Class.forName(typeName)); + } + } + return get(t); + } + + public static TypeDescription getTypeDescription(Class<?> zClass) { + return getDefinitely(new Type(zClass)); + } + + public static TypeDescription getTypeDescription(Type type) + throws ClassNotFoundException + { + //TODO: synchronize on type? + TypeDescription desc = type.getTypeDescription(); + if (desc == null) { + desc = getTypeDescription(type.getTypeName()); + type.setTypeDescription(desc); + } + return desc; + } + + public static TypeDescription getTypeDescription(TypeClass typeClass) { + switch (typeClass.getValue()) { + case TypeClass.VOID_value: + return getDefinitely(Type.VOID); + + case TypeClass.BOOLEAN_value: + return getDefinitely(Type.BOOLEAN); + + case TypeClass.BYTE_value: + return getDefinitely(Type.BYTE); + + case TypeClass.SHORT_value: + return getDefinitely(Type.SHORT); + + case TypeClass.UNSIGNED_SHORT_value: + return getDefinitely(Type.UNSIGNED_SHORT); + + case TypeClass.LONG_value: + return getDefinitely(Type.LONG); + + case TypeClass.UNSIGNED_LONG_value: + return getDefinitely(Type.UNSIGNED_LONG); + + case TypeClass.HYPER_value: + return getDefinitely(Type.HYPER); + + case TypeClass.UNSIGNED_HYPER_value: + return getDefinitely(Type.UNSIGNED_HYPER); + + case TypeClass.FLOAT_value: + return getDefinitely(Type.FLOAT); + + case TypeClass.DOUBLE_value: + return getDefinitely(Type.DOUBLE); + + case TypeClass.CHAR_value: + return getDefinitely(Type.CHAR); + + case TypeClass.STRING_value: + return getDefinitely(Type.STRING); + + case TypeClass.TYPE_value: + return getDefinitely(Type.TYPE); + + case TypeClass.ANY_value: + return getDefinitely(Type.ANY); + + default: + return null; + } + } + + public static boolean isTypeClassSimple(TypeClass typeClass) { + return getTypeDescription(typeClass) != null; + } + + /** + * Gets the <code>TypeDescription</code> of the + * super, if it exists. + * @return the <code>TypeDescription</code>. + */ + public TypeDescription getSuperType() { + // Arbitrarily take the first super type: + return superTypes == null || superTypes.length == 0 + ? null : superTypes[0]; + } + + /** + * Gets the <code>MethodDescription</code> for every + * method, if this type is an interface. Otherwise + * returns <code>null</code>. + * @return the <code>MethodDescription[]</code>. + */ + public MethodDescription[] getMethodDescriptions() { + initMethodDescriptions(); + return methodDescriptions; //TODO: clone? + } + + /** + * Gets the <code>MethodDescription</code> for the + * method with index methodId, if it exists, otherwise + * returns <code>null</code>. + * + * @param methodId the index. + * + * @return the <code>MethodDescription</code>. + */ + public MethodDescription getMethodDescription(int methodId) { + initMethodDescriptions(); + return methodId < 0 + ? null + : methodId < superMethodDescriptions.length + ? superMethodDescriptions[methodId] + : (methodId - superMethodDescriptions.length + < methodDescriptions.length) + ? methodDescriptions[methodId - superMethodDescriptions.length] + : null; + } + + /** + * Gets the <code>MethodDescription</code> for the + * method with the name <code>name</code>, if it exists, + * otherwise returns <code>null</code>. + * + * @param name the name of the method. + * + * @return the <code>MethodDescription</code>. + */ + public MethodDescription getMethodDescription(String name) { + initMethodDescriptions(); + for (int i = 0; i < superMethodDescriptions.length; ++i) { + if (superMethodDescriptions[i].getName().equals(name)) { + return superMethodDescriptions[i]; + } + } + for (int i = 0; i < methodDescriptions.length; ++i) { + if (methodDescriptions[i].getName().equals(name)) { + return methodDescriptions[i]; + } + } + return null; + } + + /** + * Gets the <code>FieldDescription</code> for every + * field, if this type is an interface. Otherwise + * returns <code>null</code>. + * @return the <code>FieldDescription[]</code>. + */ + public FieldDescription[] getFieldDescriptions() { + return fieldDescriptions; //TODO: clone? + } + + /** + * Gets the <code>FieldDescription</code> for the + * field with the name <code>name</code>, if it exists, + * otherwise returns <code>null</code>. + * + * @param name the name of the field. + * + * @return the <code>FieldDescription</code>. + */ + public FieldDescription getFieldDescription(String name) { + for (int i = 0; i < fieldDescriptions.length; ++i) { + if (fieldDescriptions[i].getName().equals(name)) { + return fieldDescriptions[i]; + } + } + return superTypes != null && superTypes.length == 1 + ? superTypes[0].getFieldDescription(name) : null; + } + + /** + * Gets the IDL <code>TypeClass</code> of the type. + * @return the <code>TypeClass</code>. + */ + public TypeClass getTypeClass() { + return typeClass; + } + + /** + * Gets the component <code>TypeDescription</code> if + * this is an array type, otherwise returns <code>null</code>. + * @return the <code>TypeDescription</code> + */ + public TypeDescription getComponentType() { + return componentType; + } + + /** + * Gets the (UNO) type name. + * <table> + * <caption>Mapping from UNO types to type names</caption> + * <thead> + * <tr><th>UNO type</th><th>type name</th></tr> + * </thead> + * <tbody> + * <tr><td>VOID</td><td><code>"void"</code></td></tr> + * <tr><td>BOOLEAN</td><td><code>"boolean"</code></td></tr> + * <tr><td>CHAR</td><td><code>"char"</code></td></tr> + * <tr><td>BYTE</td><td><code>"byte"</code></td></tr> + * <tr><td>SHORT</td><td><code>"short"</code></td></tr> + * <tr> + * <td>UNSIGNED SHORT</td><td><code>"unsigned short"</code></td> + * </tr> + * <tr><td>LONG</td><td><code>"long"</code></td></tr> + * <tr><td>UNSIGNED LONG</td><td><code>"unsigned long"</code></td></tr> + * <tr><td>HYPER</td><td><code>"hyper"</code></td></tr> + * <tr> + * <td>UNSIGNED HYPER</td><td><code>"unsigned hyper"</code></td> + * </tr> + * <tr><td>FLOAT</td><td><code>"float"</code></td></tr> + * <tr><td>DOUBLE</td><td><code>"double"</code></td></tr> + * <tr><td>STRING</td><td><code>"string"</code></td></tr> + * <tr><td>TYPE</td><td><code>"type"</code></td></tr> + * <tr><td>ANY</td><td><code>"any"</code></td></tr> + * <tr> + * <td>sequence type of base type <var>T</var></td> + * <td><code>"[]"</code> followed by type name for <var>T</var></td> + * </tr> + * <tr> + * <td>enum type named <var>N</var></td> + * <td><var>N</var> (see below)</td> + * </tr> + * <tr> + * <td>struct type named <var>N</var></td> + * <td><var>N</var> (see below)</td> + * </tr> + * <tr> + * <td>exception type named <var>N</var></td> + * <td><var>N</var> (see below)</td> + * </tr> + * <tr> + * <td>interface type named <var>N</var></td> + * <td><var>N</var> (see below)</td> + * </tr> + * </tbody> + * </table> + * <p>For a UNO type named <var>N</var>, consisting of a sequence of module + * names <var>M<sub>1</sub></var>, ..., <var>M<sub>n</sub></var> followed by + * a simple name <var>S</var>, the corresponding type name consists of the + * same sequence of module names and simple name, with <code>"."</code> + * separating the individual elements.</p> + * @return the type name. + */ + public String getTypeName() { + return typeName; + } + + /** + * Gets the (Java) array type name. + * <p>The array type name is defined to be the Java class name (as returned + * by <code>Class.forName</code>) of the Java array class that corresponds + * to the UNO sequence type with this type (the UNO type represented by this + * <code>TypeDescription</code> instance) as base type. For an + * <code>TypeDescription</code> instance representing the UNO type VOID, + * the array type name is defined to be + * <code>"[Ljava.lang.Void;"</code>.</p> + * @return the array type name. + */ + public String getArrayTypeName() { + return arrayTypeName; + } + + /** + * Gets the corresponding java class for the type. + * @return the corresponding java class. + */ + public Class<?> getZClass() { + return zClass; + } + + public boolean hasTypeArguments() { + return hasTypeArguments; + } + + // @see Object#toString + @Override + public String toString() { + return "[" + getClass().getName() + ": " + getTypeClass() + ", " + + getTypeName() + "]"; + } + + private static TypeDescription getDefinitely(Type type) { + try { + return get(type); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("this cannot happen: " + e); + } + } + + private static TypeDescription get(Type type) throws ClassNotFoundException + { + String typeName = type.getTypeName(); + TypeDescription desc = cache.get(typeName); + if (desc == null) { + desc = create(type); + cache.put(desc); + } + return desc; + } + + private static TypeDescription create(Type type) + throws ClassNotFoundException + { + TypeClass typeClass = type.getTypeClass(); + String typeName = type.getTypeName(); + Class<?> zClass = type.getZClass(); + if (zClass == null) { + throw new ClassNotFoundException("UNO type " + type); + } + switch (typeClass.getValue()) { + case TypeClass.VOID_value: + return new TypeDescription( + typeClass, typeName, "[Ljava.lang.Void;", zClass, null, null); + + case TypeClass.BOOLEAN_value: + return new TypeDescription( + typeClass, typeName, "[Z", zClass, null, null); + + case TypeClass.BYTE_value: + return new TypeDescription( + typeClass, typeName, "[B", zClass, null, null); + + case TypeClass.SHORT_value: + case TypeClass.UNSIGNED_SHORT_value: + return new TypeDescription( + typeClass, typeName, "[S", zClass, null, null); + + case TypeClass.LONG_value: + case TypeClass.UNSIGNED_LONG_value: + return new TypeDescription( + typeClass, typeName, "[I", zClass, null, null); + + case TypeClass.HYPER_value: + case TypeClass.UNSIGNED_HYPER_value: + return new TypeDescription( + typeClass, typeName, "[J", zClass, null, null); + + case TypeClass.FLOAT_value: + return new TypeDescription( + typeClass, typeName, "[F", zClass, null, null); + + case TypeClass.DOUBLE_value: + return new TypeDescription( + typeClass, typeName, "[D", zClass, null, null); + + case TypeClass.CHAR_value: + return new TypeDescription( + typeClass, typeName, "[C", zClass, null, null); + + case TypeClass.STRING_value: + return new TypeDescription( + typeClass, typeName, "[Ljava.lang.String;", zClass, null, null); + + case TypeClass.TYPE_value: + return new TypeDescription( + typeClass, typeName, "[Lcom.sun.star.uno.Type;", zClass, null, + null); + + case TypeClass.ANY_value: + return new TypeDescription( + typeClass, typeName, "[Ljava.lang.Object;", zClass, null, null); + + case TypeClass.SEQUENCE_value: + { + // assert typeName.startsWith("[]"); + TypeDescription componentType = getTypeDescription( + typeName.substring("[]".length())); + // assert zClass.getName().startsWith("["); + return new TypeDescription( + typeClass, typeName, "[" + zClass.getName(), zClass, null, + componentType); + } + + case TypeClass.ENUM_value: + // assert !zClass.getName().startsWith("["); + return new TypeDescription( + typeClass, typeName, "[L" + zClass.getName() + ";", zClass, + null, null); + + case TypeClass.STRUCT_value: + { + // This code exploits the fact that an instantiated polymorphic + // struct type may not be the direct base of a struct type: + Class<?> superClass = zClass.getSuperclass(); + TypeDescription[] superTypes = superClass != Object.class + ? new TypeDescription[] { get(new Type(superClass)) } + : null; + // assert !zClass.getName().startsWith("["); + return new TypeDescription( + typeClass, typeName, "[L" + zClass.getName() + ";", zClass, + superTypes, null); + } + + case TypeClass.EXCEPTION_value: + { + TypeDescription[] superTypes + = typeName.equals("com.sun.star.uno.Exception") + || typeName.equals("com.sun.star.uno.RuntimeException") + ? null + : new TypeDescription[] { + get(new Type(zClass.getSuperclass())) }; + // assert !zClass.getName().startsWith("["); + return new TypeDescription( + typeClass, typeName, "[L" + zClass.getName() + ";", zClass, + superTypes, null); + } + + case TypeClass.INTERFACE_value: + { + List superTypes = new List(); + Class<?>[] interfaces = zClass.getInterfaces(); + for (int i = 0; i < interfaces.length; ++i) { + Type t = new Type(interfaces[i]); + if (t.getTypeClass() == TypeClass.INTERFACE) { + TypeDescription desc = getDefinitely(t); + TypeDescription[] descs = desc.superTypes; + for (int j = 0; j < descs.length; ++j) { + superTypes.add(descs[j]); + } + superTypes.add(desc); + } + } + // assert !zClass.getName().startsWith("["); + return new TypeDescription( + typeClass, typeName, "[L" + zClass.getName() + ";", zClass, + superTypes.toArray(), null); + } + + default: + throw new IllegalArgumentException("given type has bad type class"); + } + } + + private TypeDescription( + TypeClass typeClass, String typeName, String arrayTypeName, + Class<?> zClass, TypeDescription[] superTypes, + TypeDescription componentType) + { + this.typeClass = typeClass; + this.typeName = typeName; + this.arrayTypeName = arrayTypeName; + this.zClass = zClass; + this.superTypes = superTypes; + this.componentType = componentType; + TypeDescription[] args = calculateTypeArguments(); + this.hasTypeArguments = args != null; + this.fieldDescriptions = calculateFieldDescriptions(args); + // methodDescriptions must be initialized lazily, to avoid problems with + // circular dependencies (a super-interface that has a sub-interface as + // method parameter type; an interface that has a struct as method + // parameter type, and the struct has the interface as member type) + } + + private synchronized void initMethodDescriptions() { + if (methodDescriptions != null || typeClass != TypeClass.INTERFACE) { + return; + } + if (superTypes.length == 0) { // com.sun.star.uno.XInterface + superMethodDescriptions = new MethodDescription[0]; + methodDescriptions = new MethodDescription[] { + new MethodDescription( + "queryInterface", MethodDescription.ID_QUERY_INTERFACE, + false, new TypeDescription[] { getDefinitely(Type.TYPE) }, + new TypeDescription[] { null }, getDefinitely(Type.ANY), + null), + new MethodDescription( + "acquire", MethodDescription.ID_ACQUIRE, true, + new TypeDescription[0], new TypeDescription[0], + getDefinitely(Type.VOID), null), + new MethodDescription( + "release", MethodDescription.ID_RELEASE, true, + new TypeDescription[0], new TypeDescription[0], + getDefinitely(Type.VOID), null) }; + } else { + int methodOffset = 0; + ArrayList<MethodDescription> superList = new ArrayList<MethodDescription>(); + for (int i = 0; i < superTypes.length; ++i) { + MethodDescription[] ds = superTypes[i].getMethodDescriptions(); + for (int j = 0; j < ds.length; ++j) { + superList.add(new MethodDescription(ds[j], methodOffset++)); + } + } + superMethodDescriptions = superList.toArray( + new MethodDescription[superList.size()]); + ArrayList<MethodDescription> directList = new ArrayList<MethodDescription>(); + TypeInfo[] infos = getTypeInfo(); + int infoCount = infos == null ? 0 : infos.length; + int index = 0; + Method[] methods = zClass.getDeclaredMethods(); + for (int i = 0; i < infoCount;) { + if (infos[i] instanceof AttributeTypeInfo) { + AttributeTypeInfo info = (AttributeTypeInfo) infos[i++]; + if (info.getIndex() != index) { + throw new IllegalArgumentException( + "Bad UNOTYPEINFO for " + zClass + + ": entries not ordererd"); + } + String getterName = "get" + info.getName(); + Method getter = findMethod(methods, getterName); + Type t = info.getUnoType(); + TypeDescription type = t == null + ? getTypeDescription(getter.getReturnType(), info) + : getDefinitely(t); + directList.add( + new MethodDescription( + getterName, index++ + methodOffset, false, + new TypeDescription[0], new TypeDescription[0], + type, getter)); + if (!info.isReadOnly()) { + String setterName = "set" + info.getName(); + Method setter = findMethod(methods, setterName); + directList.add( + new MethodDescription( + setterName, index++ + methodOffset, false, + new TypeDescription[] { type }, + new TypeDescription[] { null }, + getDefinitely(Type.VOID), setter)); + } + } else { + MethodTypeInfo info = (MethodTypeInfo) infos[i++]; + if (info.getIndex() != index) { + throw new IllegalArgumentException( + "Bad UNOTYPEINFO for " + zClass + + ": entries not ordererd"); + } + Method method = findMethod(methods, info.getName()); + Class<?>[] params = method.getParameterTypes(); + TypeDescription[] in = new TypeDescription[params.length]; + TypeDescription[] out + = new TypeDescription[params.length]; + for (int j = 0; j < params.length; ++j) { + ParameterTypeInfo p = null; + if (i < infoCount + && infos[i] instanceof ParameterTypeInfo + && ((ParameterTypeInfo) infos[i]).getIndex() == j) + { + p = (ParameterTypeInfo) infos[i++]; + } + Type pt = p == null ? null : p.getUnoType(); + TypeDescription d = pt == null + ? getTypeDescription(params[j], p) + : getDefinitely(pt); + if (p == null || p.isIN()) { + in[j] = d; + } + if (p != null && p.isOUT()) { + out[j] = d; + } + } + Type t = info.getUnoType(); + directList.add( + new MethodDescription( + info.getName(), index++ + methodOffset, + info.isOneway(), in, out, + (t == null + ? getTypeDescription(method.getReturnType(), info) + : getDefinitely(t)), + method)); + } + } + methodDescriptions = directList.toArray( + new MethodDescription[directList.size()]); + } + } + + private TypeDescription[] calculateTypeArguments() { + if (typeClass != TypeClass.STRUCT) { + return null; + } + int i = typeName.indexOf('<'); + if (i < 0) { + return null; + } + java.util.List<TypeDescription> args = new java.util.ArrayList<TypeDescription>(); + do { + ++i; // skip '<' or ',' + int j = i; + loop: + for (int level = 0; j != typeName.length(); ++j) { + switch (typeName.charAt(j)) { + case ',': + if (level == 0) { + break loop; + } + break; + + case '<': + ++level; + break; + + case '>': + if (level == 0) { + break loop; + } + --level; + break; + } + } + if (j != typeName.length()) { + Type t = new Type(typeName.substring(i, j)); + if (t.getZClass() == null) { + throw new IllegalArgumentException( + "UNO type name \"" + typeName + + "\" contains bad type argument \"" + + typeName.substring(i, j) + "\""); + } + args.add(getDefinitely(t)); + } + i = j; + } while (i != typeName.length() && typeName.charAt(i) != '>'); + if (i != typeName.length() - 1 || typeName.charAt(i) != '>' + || args.isEmpty()) + { + throw new IllegalArgumentException( + "UNO type name \"" + typeName + "\" is syntactically invalid"); + } + return args.toArray( + new TypeDescription[args.size()]); + } + + private FieldDescription[] calculateFieldDescriptions( + TypeDescription[] typeArguments) + { + if (typeClass != TypeClass.STRUCT && typeClass != TypeClass.EXCEPTION) { + return null; + } + TypeInfo[] infos = getTypeInfo(); + int infoCount = infos == null ? 0 : infos.length; + TypeDescription superType = getSuperType(); + FieldDescription[] superDescs = superType == null + ? null : superType.getFieldDescriptions(); + int superCount = superDescs == null ? 0 : superDescs.length; + FieldDescription[] descs = new FieldDescription[ + superCount + infoCount]; + if (superCount != 0) { + System.arraycopy(superDescs, 0, descs, 0, superCount); + } + for (int i = 0; i < infoCount; ++i) { + MemberTypeInfo info = (MemberTypeInfo) infos[i]; + if (info.getIndex() != i) { + throw new IllegalArgumentException( + "Bad UNOTYPEINFO for " + zClass + ": entries not ordererd"); + } + Field field; + try { + field = zClass.getDeclaredField(info.getName()); + } catch (NoSuchFieldException e) { + throw new IllegalArgumentException( + "Bad UNOTYPEINFO for " + zClass + ": " + e); + } + Type t = info.getUnoType(); + int index = info.getTypeParameterIndex(); + descs[i + superCount] = new FieldDescription( + info.getName(), i + superCount, + (index >= 0 + ? typeArguments[index] + : t == null + ? getTypeDescription(field.getType(), info) + : getDefinitely(t)), + field); + } + return descs; + } + + private TypeInfo[] getTypeInfo() { + try { + return (TypeInfo[]) + zClass.getDeclaredField("UNOTYPEINFO").get(null); + } catch (NoSuchFieldException e) { + return null; + } catch (IllegalAccessException e) { + throw new IllegalArgumentException( + "Bad UNOTYPEINFO for " + zClass + ": " + e); + } + } + + private Method findMethod(Method[] methods, String name) { + for (int i = 0; i < methods.length; ++i) { + if (methods[i].getName().equals(name)) { + return methods[i]; + } + } + throw new IllegalArgumentException( + "Bad UNOTYPEINFO for " + zClass + ": no method " + name); + } + + private static TypeDescription getTypeDescription( + Class<?> zClass, TypeInfo typeInfo) + { + return getDefinitely( + new Type( + zClass, + typeInfo != null + && (typeInfo.isUnsigned() || typeInfo.isInterface()))); + } + + private static final class List { + + public void add(TypeDescription desc) { + if (!list.contains(desc)) { + list.add(desc); + } + } + + public TypeDescription[] toArray() { + return list.toArray( + new TypeDescription[list.size()]); + } + + private final ArrayList<TypeDescription> list = new ArrayList<TypeDescription>(); + } + + private static final class Cache { + + public TypeDescription get(String typeName) { + synchronized (map) { + cleanUp(); + Entry e = map.get(typeName); + return e == null ? null : (TypeDescription) e.get(); + } + } + + public void put(TypeDescription desc) { + synchronized (map) { + cleanUp(); + map.put(desc.getTypeName(), new Entry(desc, queue)); + } + } + + private void cleanUp() { + for (;;) { + Object tmp = queue.poll(); + Entry e = (Entry)tmp; + if (e == null) { + break; + } + map.remove(e.typeName); + } + } + + private static final class Entry extends SoftReference<TypeDescription> { + public Entry(TypeDescription desc, ReferenceQueue<TypeDescription> queue) { + super(desc, queue); + typeName = desc.getTypeName(); + } + + public final String typeName; + } + + private final HashMap<String, Entry> map = new HashMap<String, Entry>(); + private final ReferenceQueue<TypeDescription> queue = new ReferenceQueue<TypeDescription>(); + } + + private static final Cache cache = new Cache(); + + private final TypeClass typeClass; + private final String typeName; + private final String arrayTypeName; + private final Class<?> zClass; + private final TypeDescription[] superTypes; + private final TypeDescription componentType; + private final boolean hasTypeArguments; + private final FieldDescription[] fieldDescriptions; + private MethodDescription[] methodDescriptions = null; + private MethodDescription[] superMethodDescriptions; +} diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/AttributeTypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/AttributeTypeInfo.java new file mode 100644 index 000000000..3230528e1 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/typeinfo/AttributeTypeInfo.java @@ -0,0 +1,86 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.typeinfo; + +import com.sun.star.uno.Type; + +public class AttributeTypeInfo extends TypeInfo +{ + protected int m_index; + private final Type m_unoType; // @since UDK 3.2 + + /** + Create an attribute type info with a UNO type that cannot unambiguously + be represented as a Java 1.2 type. + + @param name the name of this attribute; must not be <code>null</code> + + @param index the index among the direct members + + @param flags any flags (<code>READONLY</code>, <code>BOUND</code>, + <code>UNSIGNED</code>, <code>ANY</code>, <code>INTERFACE</code>) + + @param unoType the exact UNO type; or <code>null</code> if the UNO type + is already unambiguously represented by the Java 1.2 type + + @since UDK 3.2 + */ + public AttributeTypeInfo(String name, int index, int flags, Type unoType) { + super(name, flags); + m_index = index; + m_unoType = unoType; + } + + public AttributeTypeInfo(String name, int index, int flags) + { + this(name, index, flags, null); + } + + public int getIndex() + { + return m_index; + } + + public boolean isReadOnly() + { + return (m_flags & TypeInfo.READONLY) != 0; + } + + /** + Returns the status of the 'bound' flag. + + @since UDK 3.2 + */ + public final boolean isBound() { + return (m_flags & TypeInfo.BOUND) != 0; + } + + /** + Get the exact UNO type of this attribute type info, in case it cannot + unambiguously be represented as a Java 1.2 type. + + @return the exact UNO type of this attribute type info, or + <code>null</code> if the UNO type is already unambiguously represented by + the Java 1.2 type + + @since UDK 3.2 + */ + public final Type getUnoType() { + return m_unoType; + } +} diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/ConstantTypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/ConstantTypeInfo.java new file mode 100644 index 000000000..2676718cf --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/typeinfo/ConstantTypeInfo.java @@ -0,0 +1,32 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.typeinfo; + +/** + @deprecated <code>UNOTYPEINFO</code> for constants is not needed + */ +@Deprecated +public class ConstantTypeInfo extends TypeInfo +{ + public ConstantTypeInfo(String name, int flags) + { + super(name, flags); + } +} + + diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/MemberTypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/MemberTypeInfo.java new file mode 100644 index 000000000..5b3f669fe --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/typeinfo/MemberTypeInfo.java @@ -0,0 +1,96 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.typeinfo; + +import com.sun.star.uno.Type; + +public class MemberTypeInfo extends TypeInfo +{ + int m_index; + private final Type m_unoType; // @since UDK 3.2 + private final int m_typeParameterIndex; // @since UDK 3.2 + + /** + Create a member type info with a UNO type that cannot unambiguously be + represented as a Java 1.2 type. + + @param name the name of this member; must not be <code>null</code> + + @param index the index among the direct members + + @param flags any flags (<code>UNSIGNED</code>, <code>ANY</code>, + <code>INTERFACE</code>, <code>TYPE_PARAMETER</code>) + + @param unoType the exact UNO type; or <code>null</code> if the UNO type + is already unambiguously represented by the Java 1.2 type + + @param typeParameterIndex the index of the type parameter that determines + the type of this parameterized member; or <code>-1</code> if this member + is of an explicit type, or is the member of a plain struct type + + @since UDK 3.2 + */ + public MemberTypeInfo( + String name, int index, int flags, Type unoType, int typeParameterIndex) + { + super(name, flags); + m_index = index; + m_unoType = unoType; + m_typeParameterIndex = typeParameterIndex; + } + + public MemberTypeInfo(String name, int index, int flags ) + { + this(name, index, flags, null, -1); + } + + public int getIndex() + { + return m_index; + } + + /** + Get the exact UNO type of this member type info, in case it cannot + unambiguously be represented as a Java 1.2 type. + + @return the exact UNO type of this member type info, or <code>null</code> + if the UNO type is already unambiguously represented by the Java 1.2 + type + + @since UDK 3.2 + */ + public final Type getUnoType() { + return m_unoType; + } + + /** + Returns the index of the type parameter that determines the parameterized + type of this member. + + @return the index of the type parameter that determines the type of this + parameterized member; if this member is of an explicit type, or is the + member of a plain struct type, <code>-1</code> is returned + + @since UDK 3.2 + */ + public final int getTypeParameterIndex() { + return m_typeParameterIndex; + } +} + + diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/MethodTypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/MethodTypeInfo.java new file mode 100644 index 000000000..319e63514 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/typeinfo/MethodTypeInfo.java @@ -0,0 +1,89 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.typeinfo; + +import com.sun.star.uno.Type; + +public class MethodTypeInfo extends TypeInfo +{ + protected int m_index; + private final Type m_unoType; // @since UDK 3.2 + + /** + Create a method type info with a UNO return type that cannot + unambiguously be represented as a Java 1.2 type. + + @param name the name of this method; must not be <code>null</code> + + @param index the index among the direct members + + @param flags any flags (<code>ONEWAY</code>, <code>UNSIGNED</code>, + <code>ANY</code>, <code>INTERFACE</code>) + + @param unoType the exact UNO return type; or <code>null</code> if the UNO + type is already unambiguously represented by the Java 1.2 type + + @since UDK 3.2 + */ + public MethodTypeInfo(String name, int index, int flags, Type unoType) { + super(name, flags); + m_index = index; + m_unoType = unoType; + } + + public MethodTypeInfo(String name, int index, int flags) + { + this(name, index, flags, null); + } + + public int getIndex() + { + return m_index; + } + + public boolean isReturnUnsigned() + { + return isUnsigned(); + } + + public boolean isOneway() + { + return (m_flags & TypeInfo.ONEWAY) != 0; + } + + public boolean isConst() + { + return (m_flags & TypeInfo.CONST) != 0; + } + + /** + Get the exact UNO return type of this method type info, in case it cannot + unambiguously be represented as a Java 1.2 type. + + @return the exact UNO return type of this method type info, or + <code>null</code> if the UNO type is already unambiguously represented by + the Java 1.2 type + + @since UDK 3.2 + */ + public final Type getUnoType() { + return m_unoType; + } +} + + diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/ParameterTypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/ParameterTypeInfo.java new file mode 100644 index 000000000..f15ddc816 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/typeinfo/ParameterTypeInfo.java @@ -0,0 +1,105 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.typeinfo; + +import com.sun.star.uno.Type; + +public class ParameterTypeInfo extends TypeInfo +{ + protected int m_index; + protected String m_methodName; + private final Type m_unoType; // @since UDK 3.2 + + /** + Create a parameter type info with a UNO type that cannot unambiguously be + represented as a Java 1.2 type. + + @param name the name of this parameter; must not be <code>null</code> + + @param methodName the name of the method; must not be <code>null</code> + + @param index the index among the parameters + + @param flags any flags (<code>IN</code>, <code>OUT</code>, + <code>UNSIGNED</code>, <code>ANY</code>, <code>INTERFACE</code>) + + @param unoType the exact UNO type; or <code>null</code> if the UNO type + is already unambiguously represented by the Java 1.2 type + + @since UDK 3.2 + */ + public ParameterTypeInfo( + String name, String methodName, int index, int flags, Type unoType) + { + super(name, flags); + m_index = index; + m_methodName = methodName; + m_unoType = unoType; + } + + public ParameterTypeInfo(String name, String methodName, int index, int flags) + { + this(name, methodName, index, flags, null); + } + + public String getMethodName() + { + return m_methodName; + } + + public int getIndex() + { + return m_index; + } + + public boolean isIN() + { + return ((m_flags & TypeInfo.IN) != 0 || + (m_flags & (TypeInfo.IN | TypeInfo.OUT)) == 0); // nothing set => IN + } + + public boolean isOUT() + { + return (m_flags & TypeInfo.OUT) != 0; + } + + public boolean isINOUT() + { + return (m_flags & (TypeInfo.IN | TypeInfo.OUT)) == (TypeInfo.IN | TypeInfo.OUT); + } + + /** + Get the exact UNO type of this parameter type info, in case it cannot + unambiguously be represented as a Java 1.2 type. + + <p>If this is an out or in–out parameter, the UNO type must be a + sequence type, taking into account that such a parameter is represented + in Java as a parameter of array type.</p> + + @return the exact UNO type of this parameter type info, or + <code>null</code> if the UNO type is already unambiguously represented by + the Java 1.2 type + + @since UDK 3.2 + */ + public final Type getUnoType() { + return m_unoType; + } +} + + diff --git a/ridljar/com/sun/star/lib/uno/typeinfo/TypeInfo.java b/ridljar/com/sun/star/lib/uno/typeinfo/TypeInfo.java new file mode 100644 index 000000000..2503029fe --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/typeinfo/TypeInfo.java @@ -0,0 +1,76 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.uno.typeinfo; + + +/** Defines a class to describe additional type information. + */ +public class TypeInfo +{ + public static final int IN = 0x00000001; + public static final int OUT = 0x00000002; + public static final int UNSIGNED = 0x00000004; + public static final int READONLY = 0x00000008; + public static final int ONEWAY = 0x00000010; + public static final int CONST = 0x00000020; + public static final int ANY = 0x00000040; + public static final int INTERFACE = 0x00000080; + + /** + Marks an extended attribute of an interface type as bound. + + <p>Only used in the <code>flags</code> argument of the + <code>AttributeTypeInfo</code> constructors.</p> + + @since UDK 3.2 + */ + public static final int BOUND = 0x00000100; + + protected int m_flags; + protected String m_name; + + public TypeInfo(String name, int flags) + { + m_name = name; + m_flags = flags; + } + + public String getName() + { + return m_name; + } + + public int getFlags() { + return m_flags; + } + + public boolean isUnsigned() + { + return (m_flags & TypeInfo.UNSIGNED) != 0; + } + + public boolean isAny() + { + return (m_flags & TypeInfo.ANY) != 0; + } + + public boolean isInterface() + { + return (m_flags & TypeInfo.INTERFACE) != 0; + } +} diff --git a/ridljar/com/sun/star/lib/util/AsynchronousFinalizer.java b/ridljar/com/sun/star/lib/util/AsynchronousFinalizer.java new file mode 100644 index 000000000..588b8fe38 --- /dev/null +++ b/ridljar/com/sun/star/lib/util/AsynchronousFinalizer.java @@ -0,0 +1,117 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.util; + +import java.util.LinkedList; + +/** + * Helper class to asynchronously execute finalize methods. + * + * <p>Current JVMs seem not to be robust against long-running finalize methods, + * in that such long-running finalize methods may lead to OutOfMemoryErrors. + * This class mitigates the problem by asynchronously shifting the bodies of + * potentially long-running finalize methods into an extra thread. Classes that + * make use of this in their finalize methods are the proxies used in the + * intra-process JNI UNO bridge and the inter-process Java URP UNO bridge (where + * in both cases finalizers lead to synchronous UNO release calls).</p> + * + * <p>Irrespective whether JVMs are getting more mature and should no longer + * have problems with long-running finalize methods, at least the JNI UNO bridge + * needs some way to stop finalization of proxies (to C++ objects) well before + * process exit, as provided by drain().</p> + */ +public final class AsynchronousFinalizer { + public AsynchronousFinalizer() { + thread = new Thread("AsynchronousFinalizer") { + @Override + public void run() { + for (;;) { + Job j; + synchronized (queue) { + for (;;) { + if (done) { + return; + } + if (!queue.isEmpty()) { + break; + } + try { + queue.wait(); + } catch (InterruptedException e) { + return; + } + } + j = queue.remove(0); + } + try { + j.run(); + } catch (Throwable e) {} + } + } + }; + thread.start(); + } + + /** + * Add a job to be executed asynchronously. + * + * <p>The run method of the given job is called exactly once. If it terminates + * abnormally by throwing any Throwable, that is ignored.</p> + * + * @param job represents the body of some finalize method; must not be null. + */ + public void add(Job job) { + synchronized (queue) { + boolean first = queue.isEmpty(); + queue.add(job); + if (first) { + queue.notify(); + } + } + } + + public void drain() throws InterruptedException { + synchronized (queue) { + done = true; + queue.notify(); + } + // tdf#123481 Only join if we are not in our own thread, else we have a deadlock + if (Thread.currentThread() != thread) + thread.join(); + } + + /** + * An interface to represent bodies of finalize methods. + * + * Similar to <code>Runnable</code>, except that the run method may throw any + * <code>Throwable</code> (which is effectively ignored by + * <code>AsynchronousFinalizer.add</code>, similar to any <code>Throwables</code> + * raised by finalize being ignored). + */ + public interface Job { + void run() throws Throwable; + } + + private final LinkedList<Job> queue = new LinkedList<Job>(); + private final Thread thread; + private boolean done = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/util/DisposeListener.java b/ridljar/com/sun/star/lib/util/DisposeListener.java new file mode 100644 index 000000000..92d7fdc90 --- /dev/null +++ b/ridljar/com/sun/star/lib/util/DisposeListener.java @@ -0,0 +1,34 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.util; + +/** + * Counterpart to <code>DisposeNotifier</code>. + * + * @see DisposeNotifier + */ +public interface DisposeListener { + /** + * Callback fired by a <code>DisposeNotifier</code> once it is disposed. + * + * @param source the <code>DisposeNotifer</code> that fires the callback; + * will never be <code>null</code> + */ + void notifyDispose(DisposeNotifier source); +} diff --git a/ridljar/com/sun/star/lib/util/DisposeNotifier.java b/ridljar/com/sun/star/lib/util/DisposeNotifier.java new file mode 100644 index 000000000..ebe0008d0 --- /dev/null +++ b/ridljar/com/sun/star/lib/util/DisposeNotifier.java @@ -0,0 +1,44 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.util; + +/** + * Optional interface to be implemented by objects stored in a + * <code>WeakMap</code>. + * + * @see WeakMap + */ +public interface DisposeNotifier { + /** + * Adds a dispose listener, to be notified when this object is disposed. + * + * <p>It is unspecified what happens when the same listener is added + * multiple times.</p> + * + * <p>It is unspecified exactly when the <code>notifyDispose</code> callback + * is fired: immediately before the notifier is disposed, while it is in the + * process of disposing, or some time after it got disposed. But even if + * adding a listener to an already disposed notifier, the listener must + * eventually receive a <code>notifyDispose</code> callback.</p> + * + * @param listener a dispose listener, to be notified when this object is + * disposed; must not be <code>null</code> + */ + void addDisposeListener(DisposeListener listener); +} diff --git a/ridljar/com/sun/star/lib/util/NativeLibraryLoader.java b/ridljar/com/sun/star/lib/util/NativeLibraryLoader.java new file mode 100644 index 000000000..eb5c6af34 --- /dev/null +++ b/ridljar/com/sun/star/lib/util/NativeLibraryLoader.java @@ -0,0 +1,132 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.util; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * Helper functions to locate and load native files. + * + * <p>The methods in this class are designed to find the requested resources in + * as many cases as possible. They search various places, roughly from most + * specific to most general. This works well if a component is known to bring + * with it a certain resource, and that resource has to be found. However, it + * might not work very well in cases where you want to check whether a + * component brings with it a certain resource or not: a similarly named + * resource from another component might be found by the eager search + * algorithm.</p> + */ +public final class NativeLibraryLoader { + /** + * Load a system library, using a given class loader to locate the library. + * + * <p>This is similar to <code>System.loadLibrary</code>.</p> + * + * @param loader a class loader; may be null. + * @param libname the library name; how this name is mapped to a system + * library name is system dependent. + */ + public static void loadLibrary(ClassLoader loader, String libname) { + String sysname = System.mapLibraryName(libname); + // At least Oracle's 1.7.0_51 now maps to .dylib rather than .jnilib: + if (System.getProperty("os.name").startsWith("Mac") + && sysname.endsWith(".dylib")) + { + sysname + = sysname.substring(0, sysname.length() - "dylib".length()) + + "jnilib"; + } + File path = getResource(loader, sysname); + if (path == null) { + // If the library cannot be found as a class loader resource, try + // the global System.loadLibrary as a last resort: + System.loadLibrary(libname); + } else { + System.load(path.getAbsolutePath()); + } + } + + /** + * Locate a system resource, using a given class loader. + * + * <p>This is similar to <code>ClassLoader.getResource</code>, but only works + * for local resources (local files), and adds additional functionality for + * <code>URLClassLoaders</code>.</p> + * + * @param loader a class loader; may be null. + * @param name a resource name (that is, the name of a file). + * @return a File locating the resource, or null if the resource was not + * found. + */ + public static File getResource(ClassLoader loader, String name) { + if (loader != null) { + File path = UrlToFileMapper.mapUrlToFile(loader.getResource(name)); + if (path != null) { + return path; + } + } + // URLClassLoaders work on lists of URLs, which are typically URLs + // locating JAR files (scheme://auth/dir1/dir2/some.jar). The following + // code looks for resource name beside the JAR file + // (scheme://auth/dir1/dir2/name) and one directory up + // (scheme://auth/dir1/name). The second step is important in a typical + // OOo installation, where the JAR files are in the program/classes + // directory while the shared libraries are in the program directory. + if (!(loader instanceof URLClassLoader)) { + return null; + } + URL[] urls = ((URLClassLoader) loader).getURLs(); + for (int i = 0; i < urls.length; ++i) { + File path = UrlToFileMapper.mapUrlToFile(urls[i]); + if (path != null) { + File dir = path.isDirectory() ? path : path.getParentFile(); + if (dir != null) { + path = new File(dir, name); + if (path.exists()) { + return path; + } + dir = dir.getParentFile(); + if (dir != null) { + path = new File(dir, name); + if (path.exists()) { + return path; + } + // On macOS, dir is now the Resources dir, + // we want to look in Frameworks + if (System.getProperty("os.name").startsWith("Mac") + && dir.getName().equals("Resources")) { + dir = dir.getParentFile(); + path = new File(dir, "Frameworks/" + name); + if (path.exists()) { + return path; + } + } + } + } + } + } + return null; + } + + private NativeLibraryLoader() {} // do not instantiate +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/util/StringHelper.java b/ridljar/com/sun/star/lib/util/StringHelper.java new file mode 100644 index 000000000..de8b5253a --- /dev/null +++ b/ridljar/com/sun/star/lib/util/StringHelper.java @@ -0,0 +1,46 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.util; + +/** + * jurt.jar internal string helper methods. + */ +public final class StringHelper +{ + private StringHelper() {} // do not instantiate + + public static String replace(String str, char from, String to) { + StringBuffer b = new StringBuffer(); + for (int i = 0;;) { + int j = str.indexOf(from, i); + if (j == -1) { + b.append(str.substring(i)); + break; + } else { + b.append(str.substring(i, j)); + b.append(to); + i = j + 1; + } + } + return b.toString(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/util/UrlToFileMapper.java b/ridljar/com/sun/star/lib/util/UrlToFileMapper.java new file mode 100644 index 000000000..131890f54 --- /dev/null +++ b/ridljar/com/sun/star/lib/util/UrlToFileMapper.java @@ -0,0 +1,94 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ +package com.sun.star.lib.util; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLEncoder; + +/** + * Maps Java URL representations to File representations, on any Java version. + * + * This used to be used to do URL to File mapping in pre-Java1.4 days, but since + * we now require Java 1.5, it is largely unnecessary. + * + * @since UDK 3.2.8 + */ +public final class UrlToFileMapper { + + /** + * Maps Java URL representations to File representations. + * + * @param url some URL, possibly null. + * @return a corresponding File, or null on failure. + */ + public static File mapUrlToFile(URL url) { + if (url == null) { + return null; + } else { + try { + // where encodedUrl is url.toString(), but since that may contain + // unsafe characters (e.g., space, " "), it is encoded, as otherwise + // the URI constructor might throw java.net.URISyntaxException (in + // Java 1.5, URL.toURI might be used instead). + String encodedUrl = encode(url.toString()); + URI uri = new URI(encodedUrl); + try { + return new File(uri); + } catch (IllegalArgumentException e) { + return null; + } + } catch (URISyntaxException ex) { + throw new RuntimeException(ex); // should never happen + } + } + } + + private static String encode(String url) { + StringBuffer buf = new StringBuffer(url.length()); + for (int i = 0; i < url.length(); ++i) { + char c = url.charAt(i); + // The RFC 2732 <uric> characters: !$&'()*+,-./:;=?@[]_~ plus digits + // and letters; additionally, do not encode % again. + if (c >= 'a' && c <= 'z' || c >= '?' && c <= '[' + || c >= '$' && c <= ';' || c == '!' || c == '=' || c == ']' + || c == '_' || c == '~') + { + buf.append(c); + } else if (c == ' ') { + buf.append("%20"); + } else { + try { + String enc = URLEncoder.encode(Character.toString(c), "UTF-8"); + buf.append(enc); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); // should never happen + } + } + } + return buf.toString(); + } + + private UrlToFileMapper() {} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/lib/util/WeakMap.java b/ridljar/com/sun/star/lib/util/WeakMap.java new file mode 100644 index 000000000..f3b3b55a3 --- /dev/null +++ b/ridljar/com/sun/star/lib/util/WeakMap.java @@ -0,0 +1,319 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.lib.util; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A hash map that holds values of type <code>WeakReference</code>. + * + * <p>Like <code>HashMap</code>, this implementation provides all of the + * optional map operations, and permits the <code>null</code> key.</p> + * + * <p>Also like <code>HashMap</code>, this implementation is not synchronized. + * If multiple threads share an instance, and at least one of them executes any + * modifying operations on the <code>WeakMap</code>, they have to use external + * synchronization.</p> + * + * <p>Unlike other map implementations, <code>WeakMap</code> is asymmetric in + * that <code>put</code> expects the given value to be a plain object that is + * then wrapped in a <code>WeakReference</code>, while the occurrences of values + * in all other methods (<code>containsValue</code>, <code>entrySet</code>, + * <code>equals</code>, <code>get</code>, <code>hashCode</code>, + * <code>remove</code>, <code>values</code>, and also the return value of + * <code>put</code>) expect already wrapped instances of + * <code>WeakReference</code>. That is, after <code>weakMap.put("key", + * o)</code>, <code>weakMap.get("key").equals(o)</code> does not work as + * naïvely expected; neither does + * <code>weakMap1.putAll(weakMap2)</code>.</p> + * + * <p>At an arbitrary time after the <code>WeakReference</code> value of an + * entry has been cleared by the garbage collector, the entry is automatically + * removed from the map.</p> + * + * <p>Values placed into a <code>WeakMap</code> may optionally support the + * <code>DisposeNotifier</code> interface. For those that do, the associated + * <code>WeakReference</code> wrappers are automatically cleared as soon as the + * values are disposed.</p> + * + * Note that this class does not actually implement the Map interface properly, + * the type of the return value of the entrySet and values methods is wrong, + * but the "implements Map" is retained for backward compatibility. + */ +public final class WeakMap<K,V> implements Map { + + /** + * Declare the map as WeakReference instead of Entry because it makes the return + * type signatures of values() and keySet() cleaner. + */ + private final HashMap<K, WeakReference<V>> map = new HashMap<K, WeakReference<V>>(); + private final ReferenceQueue<V> queue = new ReferenceQueue<V>(); + + /** + * Constructs an empty <code>WeakMap</code>. + */ + public WeakMap() {} + + /** + * Constructs a new <code>WeakMap</code> with the same mappings as the + * specified <code>Map</code>. + * + * @param m the map whose mappings are to be placed in this map + */ + public WeakMap(Map<K,V> m) { + putAll(m); + } + + /** + * Returns the number of key–value mappings in this map. + * + * <p>This is a non-modifying operation.</p> + * + * @return the number of key–value mappings in this map + */ + public int size() { + return map.size(); + } + + /** + * Returns <code>true</code> if this map contains no key–value + * mappings. + * + * <p>This is a non-modifying operation.</p> + * + * @return <code>true</code> if this map contains no key–value + * mappings + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Returns <code>true</code> if this map contains a mapping for the + * specified key. + * + * <p>This is a non-modifying operation.</p> + * + * @param key the key whose presence in this map is to be tested + * @return <code>true</code> if this map contains a mapping for the + * specified key + */ + public boolean containsKey(/*K*/ Object key) { + return map.containsKey(key); + } + + /** + * Returns <code>true</code> if this map maps one or more keys to the + * specified value. + * + * <p>This is a non-modifying operation.</p> + * + * @param value the value whose presence in this map is to be tested + * @return <code>true</code> if this map maps one or more keys to the + * specified value + */ + public boolean containsValue(Object /*WeakReference<V>*/ value) { + return map.containsValue(value); + } + + /** + * Returns the value to which the specified key is mapped in this map, or + * <code>null</code> if the map contains no mapping for this key. + * + * <p>This is a non-modifying operation.</p> + * + * @param key the key whose associated value is to be returned + * + * @return the value to which this map maps the specified key, or + * <code>null</code> if the map contains no mapping for this key + */ + public WeakReference<V> get(/*K*/ Object key) { + return map.get(key); + } + + /** + * Associates the specified value with the specified key in this map. + * + * <p>This is a modifying operation.</p> + * + * @param key the key with which the specified value is to be associated + * @param value the value to be associated with the specified key. This + * must be a plain object, which is then wrapped in a + * <code>WeakReference</code>. + * @return previous value associated with the specified key, or + * <code>null</code> if there was no mapping for the key + */ + public Object /*WeakReference<V>*/ put(/*K*/ Object key, /*V*/ Object value) { + cleanUp(); + return map.put((K) key, new Entry<K,V>((K) key, (V) value, queue)); + } + + /** + * Removes the mapping for this key from this map if present. + * + * <p>This is a modifying operation.</p> + * + * @param key the key whose mapping is to be removed from the map + * @return previous value associated with the specified key, or + * <code>null</code> if there was no mapping for the key + */ + public Object /*WeakReference<V>*/ remove(/*K*/ Object key) { + cleanUp(); + return map.remove(key); + } + + /** + * Copies all of the mappings from the specified map to this map. + * + * <p>This is a modifying operation.</p> + * + * @param m mappings to be stored in this map. The values of those mappings + * must be plain objects, which are then wrapped in instances of + * <code>WeakReference</code>. + */ + public void putAll(Map/*<K,V>*/ m) { + cleanUp(); + for (Iterator<Map.Entry<K,V>> i = m.entrySet().iterator(); i.hasNext();) { + Map.Entry<K,V> e = i.next(); + K k = e.getKey(); + map.put(k, new Entry<K,V>(k, e.getValue(), queue)); + } + } + + /** + * Removes all mappings from this map. + * + * <p>This is a modifying operation.</p> + */ + public void clear() { + cleanUp(); + map.clear(); + } + + /** + * Returns a view of the keys contained in this map. + * + * <p>This is a non-modifying operation.</p> + * + * @return a set view of the keys contained in this map + */ + public Set<K> keySet() { + return map.keySet(); + } + + /** + * Returns a collection view of the values contained in this map. + * + * <p>This is a non-modifying operation.</p> + * + * @return a collection view of the values contained in this map + */ + public Collection<WeakReference<V>> values() { + return map.values(); + } + + /** + * Returns a collection view of the mappings contained in this map. + * + * <p>This is a non-modifying operation.</p> + * + * @return a collection view of the mappings contained in this map + */ + public Set/*<Map.Entry<K,WeakReference<V>>>*/ entrySet() { + return map.entrySet(); + } + + @Override + public boolean equals(Object o) { + return map.equals(o); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + /** + * Returns the referent of a <code>WeakReference</code>, silently handling a + * <code>null</code> argument. + * + * <p>This static method is useful to wrap around the return values of + * methods like <code>get</code>.</p> + * + * @param ref must be either an instance of <code>WeakReference</code> or + * <code>null</code> + * @return the referent of the specified <code>WeakReference</code>, or + * <code>null</code> if <code>ref</code> is <code>null</code> + */ + public static <T> T getValue(Object /*WeakReference<T>*/ ref) { + return ref == null ? null : ((WeakReference<T>) ref).get(); + } + + /** + * cleanUp() must only be called from within modifying methods. Otherwise, + * the implementations of entrySet, keySet and values would break + * (Specifically, iterating over the collections returned by those + * methods), as non-modifying methods might modify the underlying map. + **/ + private void cleanUp() { + for (;;) { + Entry<K,V> e = (Entry<K,V>) queue.poll(); + if (e == null) { + break; + } + // It is possible that an Entry e1 becomes weakly reachable, then + // another Entry e2 is added to the map for the same key, and only + // then e1 is enqueued. To not erroneously remove the new e2 in + // that case, check whether the map still contains e1: + Object k = e.key; + if (e == map.get(k)) { + map.remove(k); + } + } + } + + private static final class Entry<K,V> extends WeakReference<V> + implements DisposeListener + { + private final K key; + + private Entry(K key, V value, ReferenceQueue<V> queue) { + super(value, queue); + this.key = key; + if (value instanceof DisposeNotifier) { + ((DisposeNotifier) value).addDisposeListener(this); + } + } + + /** + * @see DisposeListener#notifyDispose(DisposeNotifier) + */ + public void notifyDispose(DisposeNotifier source) { + clear(); + enqueue(); + } + } + +} diff --git a/ridljar/com/sun/star/uno/Any.java b/ridljar/com/sun/star/uno/Any.java new file mode 100644 index 000000000..2f4976436 --- /dev/null +++ b/ridljar/com/sun/star/uno/Any.java @@ -0,0 +1,151 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + + +package com.sun.star.uno; + + +/** + * The UNO IDL type any is mapped to java type <code>java.lang.Object</code>. + * <p> + * In special cases it is necessary to have an explicit any to additionally transport + * an exact type. For instance if you want to pass an object reference via + * an interprocess connection using an any, you should use this class to add + * an explicit interface type, so the remote counterpart doesn't need to invoke + * a queryInterface). + * </p> + */ +public class Any { + /** + * The type of the any. + * + * @see #getType + */ + protected Type _type; + + /** + * The data of the any. + * + * @see #getObject + */ + protected Object _object; + + public static final Any VOID = new Any(new Type("void", TypeClass.VOID), + null); + // do not use Type.VOID here to avoid circular dependencies between + // static members of Any and Type + + /** + * Constructs a new any. + * + * @param zInterface the type of the any. + * @param object the data of the any. + * @deprecated as of UDK 2.0 + */ + @Deprecated + public Any(Class<?> zInterface, Object object) { + this(new Type(zInterface), object); + } + + /** + * Constructs a new any with a given type and value + * + * @param type the UNO type of the any. + * @param object the value of the any. + */ + public Any(Type type, Object object) { + if (type.equals(Type.ANY)) { + throw new IllegalArgumentException("Any cannot contain Any"); + } + _type = type; + _object = object; + } + + /** + * Complete a UNO <code>ANY</code> (make sure it is wrapped up as an + * <code>Any</code> instance). + * + * @param any a Java value representing a UNO <code>ANY</code> value. + * @return a complete Java value (that is, an <code>Any</code> instance) + * representing the same UNO <code>ANY</code> value as the given argument. + * @since UDK 3.2.3 + */ + public static final Any complete(Object any) { + return any instanceof Any + ? (Any) any + : new Any( + new Type(any == null ? XInterface.class : any.getClass()), any); + } + + /** + * Gets the type of the value within the any. + * + * @return the type of the value within the any. + */ + public Type getType() { + return _type; + } + + /** + * Gets the value within the any. + * + * @return gets the value within the any. + */ + public Object getObject() { + return _object; + } + + /** + * Indicates whether some other object is equal to this one. + * + * @param obj the reference object with which to compare. + * @return <code>true</code> if this object is the same as the obj argument; + * <code>false</code> otherwise. + * @see java.lang.Object#equals + */ + @Override + public boolean equals(Object obj) { + return obj instanceof Any && _type.equals(((Any) obj)._type) + && (_object == null + ? ((Any) obj)._object == null + : _object.equals(((Any) obj)._object)); + } + + /** + * Returns a hash code value for the object. + * + * @return a hash code value for this object. + * @see java.lang.Object#hashCode + */ + @Override + public int hashCode() { + return _type.hashCode() * 13 + + (_object == null ? 0 : _object.hashCode()); + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + * @see java.lang.Object#toString + */ + @Override + public String toString() { + return "Any[" + _type + ", " + _object + "]"; + } +} diff --git a/ridljar/com/sun/star/uno/AnyConverter.java b/ridljar/com/sun/star/uno/AnyConverter.java new file mode 100644 index 000000000..d36542e1a --- /dev/null +++ b/ridljar/com/sun/star/uno/AnyConverter.java @@ -0,0 +1,668 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + +/** + * This class provides static methods which aim at exploring the contents of an + * Any and extracting its value. + * + * <p>All public methods take an Object argument that either is the immediate object, + * such as Boolean, Type, interface implementation, or an Any that contains an + * object.</p> + * + * <p>The methods which extract the value do a widening conversion. See the + * method comments for the respective conversions.</p> + */ +public class AnyConverter +{ + /** + * Determines the type of an Any object. + * + * @param object any object. + * @return type object. + */ + public static Type getType( Object object ) + { + Type t; + if (null == object) + { + t = m_XInterface_type; + } + else if (object instanceof Any) + { + t = ((Any)object).getType(); + // nested any + if (TypeClass.ANY_value == t.getTypeClass().getValue()) + return getType( ((Any)object).getObject() ); + } + else + { + t = new Type( object.getClass() ); + } + return t; + } + + /** + * Checks if the any contains the idl type <code>void</code>. + * + * @param object the object to check. + * @return true when the any is void, false otherwise. + */ + public static boolean isVoid(Object object){ + return containsType(TypeClass.VOID, object); + } + + /** + * Checks if the any contains a value of the idl type <code>char</code>. + * + * @param object the object to check. + * @return true when the any contains a char, false otherwise. + */ + public static boolean isChar(Object object){ + return containsType(TypeClass.CHAR, object); + } + + /** + * Checks if the any contains a value of the idl type <code>boolean</code>. + * + * @param object the object to check. + * @return true when the any contains a boolean, false otherwise. + */ + public static boolean isBoolean(Object object){ + return containsType(TypeClass.BOOLEAN, object); + } + + /** + * Checks if the any contains a value of the idl type <code>byte</code>. + * + * @param object the object to check. + * @return true when the any contains a byte, false otherwise. + */ + public static boolean isByte(Object object){ + return containsType(TypeClass.BYTE, object); + } + + /** + * Checks if the any contains a value of the idl type <code>short</code>. + * + * @param object the object to check. + * @return true when the any contains a short, false otherwise. + */ + public static boolean isShort(Object object){ + return containsType(TypeClass.SHORT, object); + } + + /** + * Checks if the any contains a value of the idl type <code>long</code> + * (which maps to a java-int). + * + * @param object the object to check. + * @return true when the any contains a int, false otherwise. + */ + public static boolean isInt(Object object){ + return containsType(TypeClass.LONG, object); + } + + /** + * Checks if the any contains a value of the idl type <code>hyper</code> + * (which maps to a java-long). + * + * @param object the object to check. + * @return true when the any contains a long, false otherwise. + */ + public static boolean isLong(Object object){ + return containsType(TypeClass.HYPER, object); + } + + /** + * Checks if the any contains a value of the idl type <code>float</code>. + * + * @param object the object to check. + * @return true when the any contains a float, false otherwise. + */ + public static boolean isFloat(Object object){ + return containsType(TypeClass.FLOAT, object); + } + + /** + * Checks if the any contains a value of the idl type <code>double</code>. + * + * @param object the object to check. + * @return true when the any contains a double, false otherwise. + */ + public static boolean isDouble(Object object){ + return containsType(TypeClass.DOUBLE, object); + } + + /** + * Checks if the any contains a value of the idl type <code>string</code>. + * + * @param object the object to check. + * @return true when the any contains a string, false otherwise. + */ + public static boolean isString(Object object){ + return containsType(TypeClass.STRING, object); + } + + /** + * Checks if the any contains a value of the idl type <code>enum</code>. + * + * @param object the object to check. + * @return true if the any contains an enum, false otherwise. + */ + public static boolean isEnum(Object object) + { + return containsType(TypeClass.ENUM, object); + } + + /** + * Checks if the any contains a value of the idl type <code>type</code>. + * + * @param object the object to check. + * @return true when the any contains a type, false otherwise. + */ + public static boolean isType(Object object){ + return containsType(TypeClass.TYPE, object); + } + + /** + * Checks if the any contains an interface, struct, exception, sequence or enum. + * + * <p>If <em>object</em> is an any with an interface type, then true is also + * returned if the any contains a null reference. This is because interfaces + * are allowed to have a null value contrary to other UNO types.</p> + * + * @param object the object to check. + * @return true if the any contains an object. + */ + public static boolean isObject(Object object) + { + int tc = getType(object).getTypeClass().getValue(); + return (TypeClass.INTERFACE_value == tc || + TypeClass.STRUCT_value == tc || + TypeClass.EXCEPTION_value == tc || + TypeClass.SEQUENCE_value == tc || + TypeClass.ENUM_value == tc); + } + + /** + * Checks if the any contains UNO idl sequence value (meaning a java array + * containing elements which are values of UNO idl types). + * + * @param object the object to check. + * @return true when the any contains an object which implements interfaces, + * false otherwise. + */ + public static boolean isArray(Object object){ + return containsType(TypeClass.SEQUENCE, object); + } + + /** + * Converts a Char object or an Any object containing a Char object into a + * simple char. + * + * @param object the object to convert. + * @return the char contained within the object. + * @throws com.sun.star.lang.IllegalArgumentException in case no char is + * contained within object. + * + * @see #isChar + */ + public static char toChar(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Character ret= (Character)convertSimple(TypeClass.CHAR, null, object); + return ret.charValue(); + } + + /** + * Converts a Boolean object or an Any object containing a Boolean object + * into a simple boolean. + * + * @param object the object to convert. + * @return the boolean contained within the object + * @throws com.sun.star.lang.IllegalArgumentException in case no boolean is + * contained within object + * + * @see #isBoolean + */ + public static boolean toBoolean(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Boolean ret= (Boolean)convertSimple(TypeClass.BOOLEAN, null, object); + return ret.booleanValue(); + } + + /** + * Converts a Byte object or an Any object containing a Byte object into a + * simple byte. + * + * @param object the object to convert. + * @return the boolean contained within the object. + * @throws com.sun.star.lang.IllegalArgumentException in case no byte is + * contained within object. + * + * @see #isBoolean + */ + public static byte toByte(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Byte ret= (Byte)convertSimple(TypeClass.BYTE, null, object); + return ret.byteValue(); + } + + /** + * Converts a number object into a simple short and allows widening conversions. + * + * <p>Allowed argument types are Byte, Short or Any containing these types.</p> + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no short or + * byte is contained within object. + * + * @return the short contained within the object. + */ + public static short toShort(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Short ret= (Short)convertSimple(TypeClass.SHORT, null, object); + return ret.shortValue(); + } + /** + * Converts a number object into an idl unsigned short and allows widening + * conversions. + * + * <p>Allowed argument types are Anies containing idl unsigned short values.</p> + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no idl unsigned + * short is contained within Any. + * + * @return an (unsigned) short. + */ + public static short toUnsignedShort(Object object) + throws com.sun.star.lang.IllegalArgumentException + { + Short ret= (Short)convertSimple(TypeClass.UNSIGNED_SHORT, null, object); + return ret.shortValue(); + } + + /** + * Converts a number object into a simple int and allows widening conversions. + * + * <p>Allowed argument types are Byte, Short, Integer or Any containing these + * types.</p> + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no short, byte + * or int is contained within object. + * + * @return the int contained within the object. + */ + public static int toInt(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Integer ret= (Integer) convertSimple( TypeClass.LONG, null, object); + return ret.intValue(); + } + /** + * Converts a number object into an idl unsigned long and allows widening + * conversions. + * + * <p>Allowed argument types are Anies containing idl unsigned short or + * unsigned long values.</p> + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no idl unsigned + * short nor unsigned long is contained within Any. + * + * @return an (unsigned) int. + */ + public static int toUnsignedInt(Object object) + throws com.sun.star.lang.IllegalArgumentException + { + Integer ret = (Integer)convertSimple(TypeClass.UNSIGNED_LONG, null, object); + return ret.intValue(); + } + + /** + * Converts a number object into a simple long and allows widening conversions. + * + * <p>Allowed argument types are Byte, Short, Integer, Long or Any containing + * these types.</p> + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no short, byte, + * int or long is contained within object. + * + * @return the long contained within the object. + */ + public static long toLong(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Long ret= (Long) convertSimple( TypeClass.HYPER, null, object); + return ret.longValue(); + } + /** + * Converts a number object into an idl unsigned hyper and allows widening + * conversions. + * + * <p>Allowed argument types are Anies containing idl unsigned short, unsigned + * long or unsigned hyper values.</p> + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no idl unsigned + * short, nor unsigned long nor unsigned hyper is contained within object. + * + * @return an (unsigned) long. + */ + public static long toUnsignedLong(Object object) + throws com.sun.star.lang.IllegalArgumentException + { + Long ret = (Long)convertSimple(TypeClass.UNSIGNED_HYPER, null, object); + return ret.longValue(); + } + + /** + * Converts a number object into a simple float and allows widening conversions. + * + * <p>Allowed argument types are Byte, Short, Float or Any containing these + * types.</p> + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no byte, short + * or float is contained within object. + * + * @return the float contained within the object. + */ + public static float toFloat(Object object) throws com.sun.star.lang.IllegalArgumentException{ + Float ret= (Float) convertSimple( TypeClass.FLOAT,null, object); + return ret.floatValue(); + } + + /** + * Converts a number object into a simple double and allows widening conversions. + * + * <p>Allowed argument types are Byte, Short, Int, Float, Double or Any + * containing these types.</p> + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no byte, short, + * int, float or double is contained within object. + * + * @return the double contained within the object. + */ + public static double toDouble(Object object) throws com.sun.star.lang.IllegalArgumentException { + Double ret= (Double) convertSimple( TypeClass.DOUBLE, null, object); + return ret.doubleValue(); + } + + /** + * Converts a string or an any containing a string into a string. + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no string is + * contained within object. + * + * @return the string contained within the object. + */ + public static String toString(Object object) throws com.sun.star.lang.IllegalArgumentException { + return (String) convertSimple( TypeClass.STRING, null, object); + } + + /** + * Converts a Type or an any containing a Type into a Type. + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no type is + * contained within object. + * + * @return the type contained within the object. + */ + public static Type toType(Object object) throws com.sun.star.lang.IllegalArgumentException { + return (Type) convertSimple( TypeClass.TYPE, null, object); + } + + /** + * Converts a UNO object (struct, exception, sequence, enum or interface) or + * an Any containing these types into a UNO object of a specified destination + * type. + * + * <p> For interfaces, the argument <em>object</em> is queried for the interface + * specified by the <em>type</em> argument.</p> + * + * <p>That query (UnoRuntime.queryInterface) might return null, if the interface + * is not implemented or a null-ref or a VOID any is given.</p> + * + * @param type type of the returned value. + * @param object the object that is to be converted. + * @throws com.sun.star.lang.IllegalArgumentException in case conversion is + * not possible. + * + * @return destination object. + */ + public static Object toObject(Type type, Object object) + throws com.sun.star.lang.IllegalArgumentException + { + return convertSimple( type.getTypeClass(), type, object ); + } + /** + * Converts a UNO object (struct, exception, sequence, enum or interface) or + * an Any containing these types into a UNO object of a specified destination + * type. + * + * <p>For interfaces, the argument <em>object</em> is queried for the interface + * specified by the <em>type</em> argument. That query (UnoRuntime.queryInterface) + * might return null, if the interface is not implemented or a null-ref or a + * VOID any is given.</p> + * + * @param clazz class of the returned value. + * @param object the object that is to be converted. + * @throws com.sun.star.lang.IllegalArgumentException in case conversion is + * not possible. + * + * @return destination object. + */ + public static Object toObject(Class<?> clazz, Object object) + throws com.sun.star.lang.IllegalArgumentException + { + return toObject( new Type( clazz ), object ); + } + + /** + * Converts an array or an any containing an array into an array. + * + * @param object the object to convert. + * @throws com.sun.star.lang.IllegalArgumentException in case no array is + * contained within object. + * + * @return the array contained within the object. + */ + public static Object toArray( Object object) throws com.sun.star.lang.IllegalArgumentException { + return convertSimple( TypeClass.SEQUENCE, null, object); + } + + /** + * Examines the argument <em>object</em> if is correspond to the type in + * argument <em>what</em>. + * + * <p><em>object</em> is either matched directly against the type or if it is + * an any then the contained object is matched against the type.</p> + */ + private static boolean containsType( TypeClass what, Object object){ + return (getType(object).getTypeClass().getValue() == what.getValue()); + } + + private static final Type m_XInterface_type = new Type( XInterface.class ); + + private static Object convertSimple( TypeClass destTClass, Type destType, Object object_ ) + throws com.sun.star.lang.IllegalArgumentException + { + Object object; + Type type; + if (object_ instanceof Any) { + // unbox + Any a = (Any)object_; + object = a.getObject(); + type = a.getType(); + // nested any + if (TypeClass.ANY_value == type.getTypeClass().getValue()) + return convertSimple( destTClass, destType, object ); + } else { + object = object_; + type = (null == object ? m_XInterface_type : new Type( object.getClass() )); + } + + int tc = type.getTypeClass().getValue(); + int dest_tc = destTClass.getValue(); + + if (null == object) { + // special for interfaces + if (TypeClass.INTERFACE_value == tc && dest_tc == tc) + return null; + } else { + switch (dest_tc) { + case TypeClass.CHAR_value: + if (tc == TypeClass.CHAR_value) + return object; + break; + case TypeClass.BOOLEAN_value: + if (tc == TypeClass.BOOLEAN_value) + return object; + break; + case TypeClass.BYTE_value: + if (tc == TypeClass.BYTE_value) + return object; + break; + case TypeClass.SHORT_value: + switch (tc) { + case TypeClass.BYTE_value: + return Short.valueOf( ((Byte)object).byteValue() ); + case TypeClass.SHORT_value: + return object; + } + break; + case TypeClass.UNSIGNED_SHORT_value: + switch (tc) { + case TypeClass.UNSIGNED_SHORT_value: + return object; + } + break; + case TypeClass.LONG_value: + switch (tc) { + case TypeClass.BYTE_value: + return Integer.valueOf( ((Byte)object).byteValue() ); + case TypeClass.SHORT_value: + case TypeClass.UNSIGNED_SHORT_value: + return Integer.valueOf( ((Short)object).shortValue() ); + case TypeClass.LONG_value: + return object; + } + break; + case TypeClass.UNSIGNED_LONG_value: + switch (tc) { + case TypeClass.UNSIGNED_SHORT_value: + return Integer.valueOf( ((Short)object).shortValue() ); + case TypeClass.UNSIGNED_LONG_value: + return object; + } + break; + case TypeClass.HYPER_value: + switch (tc) { + case TypeClass.BYTE_value: + return Long.valueOf( ((Byte)object).byteValue() ); + case TypeClass.SHORT_value: + case TypeClass.UNSIGNED_SHORT_value: + return Long.valueOf( ((Short)object).shortValue() ); + case TypeClass.LONG_value: + case TypeClass.UNSIGNED_LONG_value: + return Long.valueOf( ((Integer)object).intValue() ); + case TypeClass.HYPER_value: + return object; + } + break; + case TypeClass.UNSIGNED_HYPER_value: + switch (tc) { + case TypeClass.UNSIGNED_SHORT_value: + return Long.valueOf( ((Short)object).shortValue() ); + case TypeClass.UNSIGNED_LONG_value: + return Long.valueOf( ((Integer)object).intValue() ); + case TypeClass.UNSIGNED_HYPER_value: + return object; + } + break; + case TypeClass.FLOAT_value: + switch (tc) { + case TypeClass.BYTE_value: + return new Float( ((Byte)object).byteValue() ); + case TypeClass.SHORT_value: + return new Float( ((Short)object).shortValue() ); + case TypeClass.FLOAT_value: + return object; + } + break; + case TypeClass.DOUBLE_value: + switch (tc) { + case TypeClass.BYTE_value: + return new Double( ((Byte)object).byteValue() ); + case TypeClass.SHORT_value: + return new Double( ((Short)object).shortValue() ); + case TypeClass.LONG_value: + return new Double( ((Integer)object).intValue() ); + case TypeClass.FLOAT_value: + return new Double( ((Float)object).floatValue() ); + case TypeClass.DOUBLE_value: + return object; + } + break; + case TypeClass.ENUM_value: + if (tc == TypeClass.ENUM_value && + (null == destType || destType.equals( type ) /* optional destType */)) + { + return object; + } + break; + case TypeClass.STRING_value: + if (tc == TypeClass.STRING_value) + return object; + break; + case TypeClass.TYPE_value: + if (tc == TypeClass.TYPE_value) + return object; + break; + case TypeClass.INTERFACE_value: + // Because object is a class, not an interface, it is + // controversial what kind of Type "new Type(object.class)" + // above should return (UNKNOWN or INTERFACE), so that we should + // not check here for "tc == TypeClass.INTERFACE_value". + // Instead, we check whether object (indirectly) derives from + // XInterface: + if (object instanceof XInterface) + return UnoRuntime.queryInterface( destType, object ); + break; + case TypeClass.STRUCT_value: + case TypeClass.EXCEPTION_value: + if (destType.isSupertypeOf(type)) { + return object; + } + break; + case TypeClass.SEQUENCE_value: + if (tc == TypeClass.SEQUENCE_value && + (null == destType || destType.equals( type ) /* optional destType */)) + { + return object; + } + break; + } + } + throw new com.sun.star.lang.IllegalArgumentException( + "The Argument did not hold the proper type"); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/uno/Ascii.java b/ridljar/com/sun/star/uno/Ascii.java new file mode 100644 index 000000000..ce5108893 --- /dev/null +++ b/ridljar/com/sun/star/uno/Ascii.java @@ -0,0 +1,43 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + +/** + * The Ascii class represents the IDL build in type <code>ascii</code>. + * + * @deprecated do not use. + */ +@Deprecated +public final class Ascii { + public final char ascii; + + /** + * Constructs a new <code>Ascii</code>. + * + * @deprecated do not use. + * @param c the char value. + */ + @Deprecated + public Ascii(char c) { + ascii = c; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/uno/AsciiString.java b/ridljar/com/sun/star/uno/AsciiString.java new file mode 100644 index 000000000..8b744c5a2 --- /dev/null +++ b/ridljar/com/sun/star/uno/AsciiString.java @@ -0,0 +1,44 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + +/** + * The Ascii class represents the IDL build in type <code>asciistring</code>. + * + * @deprecated do not use. + */ +@Deprecated +public final class AsciiString { + public final String asciistring; + + /** + * Constructs a new <code>AsciiString</code>. + * + * @deprecated do not use. + * @param s the String value. + */ + @Deprecated + public AsciiString(String s) { + asciistring = s; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/uno/Enum.java b/ridljar/com/sun/star/uno/Enum.java new file mode 100644 index 000000000..8bd50876d --- /dev/null +++ b/ridljar/com/sun/star/uno/Enum.java @@ -0,0 +1,50 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + +/** + * The Enum class is the base class for all classes generated + * as java binding for the IDL type enum. + * <p> + * Each java mapped enum class provides static member of this class + * which represents the enum values. + * You cannot create an object of this class or subclass direct, to + * avoid enum values with integer values outside the defined range. + * </p> + */ +public abstract class Enum { + private final int m_value; + + /** + * Constructs an enum value. + * @param value the integer value of this enum value. + */ + protected Enum(int value) { + m_value = value; + } + + /** + * Get the integer value of an enum value. + * @return the integer value. + */ + public final int getValue() { + return m_value; + } +} + diff --git a/ridljar/com/sun/star/uno/IBridge.java b/ridljar/com/sun/star/uno/IBridge.java new file mode 100644 index 000000000..51babcdfc --- /dev/null +++ b/ridljar/com/sun/star/uno/IBridge.java @@ -0,0 +1,94 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + +import java.io.IOException; + +/** + * This is abstract interface for bridges. + * + * <p>Bridges are able to map one object from one UNO environment to another and + * vice versa.<p> + * + * @see com.sun.star.uno.IBridge + * @see com.sun.star.uno.IQueryInterface + * @see com.sun.star.uno.UnoRuntime + * + * @deprecated As of UDK 3.2, this interface is deprecated, without offering a + * replacement. + */ +@Deprecated +public interface IBridge { + /** + * Maps an object from the source environment to the destination + * environment. + * + * @param object the object to map + * @param type the type of the interface that shall be mapped + * @return the object in the destination environment + */ + Object mapInterfaceTo(Object object, Type type); + + /** + * Maps an object from the destination environment to the source + * environment. + * + * @param object the object to map + * @param type the type of the interface that shall be mapped + * @return the object in the source environment + */ + Object mapInterfaceFrom(Object object, Type type); + + /** + * Returns the source environment. + * + * @return the source environment of this bridge + */ + IEnvironment getSourceEnvironment(); + + /** + * Returns the destination environment. + * + * @return the destination environment of this bridge + */ + IEnvironment getTargetEnvironment(); + + /** + * Increases the life count. + */ + void acquire(); + + /** + * Decreases the life count. + * + * <p>If the life count drops to zero, the bridge disposes itself.</p> + */ + void release(); + + /** + * Disposes the bridge. + * + * <p>Sends involved threads an <code>InterruptedException</code>. Releases + * mapped objects.</p> + * + * @throws InterruptedException it's deprecated so who cares. + * @throws IOException it's deprecated so who cares. + */ + void dispose() throws InterruptedException, IOException; +} diff --git a/ridljar/com/sun/star/uno/IEnvironment.java b/ridljar/com/sun/star/uno/IEnvironment.java new file mode 100644 index 000000000..5d3289305 --- /dev/null +++ b/ridljar/com/sun/star/uno/IEnvironment.java @@ -0,0 +1,144 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + +/** + * The interface implemented by UNO environments. + * + * <p>With this interface, objects can be registered at and revoked from an + * environment.</p> + * + * @see com.sun.star.uno.IBridge + * @see com.sun.star.uno.IQueryInterface + * @see com.sun.star.uno.UnoRuntime + * + * @deprecated As of UDK 3.2, this interface is deprecated, without offering a + * replacement. + */ +@Deprecated +public interface IEnvironment { + /** + * Gets the context of this environment. + * + * @return the context of this environment + */ + Object getContext(); + + /** + * Gets the name of this environment. + * + * @return the name of this environment + */ + String getName(); + + /** + * Registers one UNO interface facet of an object. + * + * <p>Such an object will typically be one of three things: + * <ul> + * <li>A local Java object, to be mapped out of this environment via a given + * bridge.</li> + * <li>A proxy object, mapped into this environment via some bridge + * <var>B1</var>, and now to be mapped out of this environment via a + * given bridge <var>B2</var>.</li> + * <li>A proxy object, created as a remote object is mapped into this + * environment via a given bridge.</li> + * </ul> + * + * <p>The object actually registered may differ from the specified + * <code>object</code> that is passed as an argument. This enables an + * environment to work in a multi-threaded scenario, where two threads can + * call <code>registerInterface</code> for the same combination of + * <code>oid</code> and <code>type</code> at the same time; the race + * condition is solved by letting one of the calls register its argument + * <code>object</code>, ignoring the argument <code>object</code> of the + * other call, and letting both calls return the same + * <code>object</code>.</p> + * + * <p>The registered object is held only weakly by the environment. After a + * call to <code>registerInterface</code>, a call to + * <code>getRegisteredInterface</code> only succeeds as long as the + * registered object is still strongly reachable, and the registered object + * has not been explicitly revoked by calling + * <code>revokeInterface</code>.</p> + * + * @param object the object to register; must be non-null + * @param oid in-out parameter containing the OID of <code>object</code>. + * This must be a non-null reference to an array of length at least one; + * the zeroth element is used to pass the argument in and out. If the + * zeroth element is null on input, the OID will be computed and passed + * out (that is, the zeroth element will never be null upon normal + * return). + * @param type the UNO interface type to register. This argument must be + * non-null, and must denote a UNO interface type. The given + * <code>object</code> should implement this <code>type</code>. + * @return the registered object (may differ from the <code>object</code> + * passed in); will never be null + */ + Object registerInterface(Object object, String[] oid, Type type); + + /** + * Explicitly revokes a UNO interface facet. + * + * <p>Calls to <code>registerInterface</code> and + * <code>revokeInterface</code> must be paired. A facet is only removed + * from the environment when it has been revoked as often as it has been + * registered. This may change in the future, so that a facet would be + * removed upon the first call to <code>revokeInterface</code> (and calls to + * <code>revokeInterface</code> would no longer be necessary if the calling + * code does not want to control the temporal extent of the + * registration).</p> + * + * <p>It is not an error if the specified facet is not registered at this + * environment (either because no corresponding object has ever been + * registered, or it has been explicitly revoked, or it is no longer + * strongly reachable). In such a case, this method simply does + * nothing.</p> + * + * @param oid the OID of the object to revoke; must be non-null + * @param type the UNO interface type of the object to revoke. This + * argument must be non-null, and must denote a UNO interface type. + */ + void revokeInterface(String oid, Type type); + + /** + * Retrieves a registered object, specified by OID and UNO interface type. + * + * @param oid the OID of the object to retrieve; must be non-null + * @param type the UNO interface type of the object to retrieve. This + * argument must be non-null, and must denote a UNO interface type. + * @return the registered object, or null if none is found + */ + Object getRegisteredInterface(String oid, Type type); + + /** + * Retrieves the OID for a registered object. + * + * @param object a registered object; must be non-null + * @return the OID of the <code>object</code>; will never be null + */ + String getRegisteredObjectIdentifier(Object object); + + /** + * Lists the registered objects to <code>System.out</code>. + * + * <p>This is for debug purposes.</p> + */ + void list(); +} diff --git a/ridljar/com/sun/star/uno/IMapping.java b/ridljar/com/sun/star/uno/IMapping.java new file mode 100644 index 000000000..fdf6d8207 --- /dev/null +++ b/ridljar/com/sun/star/uno/IMapping.java @@ -0,0 +1,41 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + +/** + * With a mapping objects can be mapped from one environment to another. + * + * <p>This interface exists for compatibility with the binary UNO API.</p> + * + * @see com.sun.star.uno.IBridge + * + * @deprecated As of UDK 3.2, this interface is deprecated, without offering a + * replacement. + */ +@Deprecated +public interface IMapping { + /** + * Maps an interface from one environment to another. + * + * @param object source object that is to be mapped + * @param type description of the interface that is to be mapped + * @return the object mapped to the destination environment + */ + Object mapInterface(Object object, Type type); +} diff --git a/ridljar/com/sun/star/uno/IQueryInterface.java b/ridljar/com/sun/star/uno/IQueryInterface.java new file mode 100644 index 000000000..7857d6205 --- /dev/null +++ b/ridljar/com/sun/star/uno/IQueryInterface.java @@ -0,0 +1,60 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + +/** + * This is the delegator interface for Java objects implementing interfaces of + * an underlying UNO object. + * + * <p>Calls are delegated through the <code>UnoRuntime</code> to this + * interface. Implement this interface in case you want to customize the + * behaviour of <code>UnoRuntime.queryInterface</code>.</p> + * + * @see com.sun.star.uno.UnoRuntime + */ +public interface IQueryInterface { + /** + * Returns the unique object identifier (OID) of the underlying UNO object. + * + * @return the OID of the underlying object + */ + String getOid(); + + /** + * Returns an object implementing the requested interface type. + * + * @param type the requested UNO interface type; must be a <code>Type</code> + * object representing a UNO interface type + * @return a reference to the requested UNO interface type if available, + * otherwise <code>null</code> + * @see com.sun.star.uno.UnoRuntime + */ + Object queryInterface(Type type); + + /** + * Tests if the given reference represents a facet of the underlying UNO + * object. + * + * @param object a reference to any Java object representing (a facet of) a + * UNO object; may be <code>null</code> + * @return <code>true</code> if and only if <code>object</code> is not + * <code>null</code> and represents the same UNO object as this object + */ + boolean isSame(Object object); +} diff --git a/ridljar/com/sun/star/uno/MappingException.java b/ridljar/com/sun/star/uno/MappingException.java new file mode 100644 index 000000000..7670a790a --- /dev/null +++ b/ridljar/com/sun/star/uno/MappingException.java @@ -0,0 +1,65 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + + +/** + * The mapping Exception. + * + * <p>The exception is replaced by the com.sun.star.lang.DisposedException.</p> + * + * @see com.sun.star.uno.UnoRuntime + * @see com.sun.star.uno.IQueryInterface + * @see com.sun.star.uno.IBridge + * + * @deprecated since UDK 3.0.2 + */ +@Deprecated +public class MappingException extends com.sun.star.uno.RuntimeException { + /** + * Constructs an empty <code>MappingException</code>. + */ + public MappingException() { + super(); + } + + /** + * Constructs an <code>MappingException</code> with a detail message. + * + * @param message the detail message. + */ + public MappingException(String message) { + super(message); + } + + /** + * Constructs an <code>MappingException</code> with a detail message and a + * context. + * + * @param message the detail message. + * @param context the context. + */ + public MappingException(String message, Object context) { + super(message, context); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ridljar/com/sun/star/uno/Type.java b/ridljar/com/sun/star/uno/Type.java new file mode 100644 index 000000000..0589b64cb --- /dev/null +++ b/ridljar/com/sun/star/uno/Type.java @@ -0,0 +1,699 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + +import java.util.HashMap; + +import com.sun.star.lib.uno.typedesc.TypeDescription; + +/** + * Represents the UNO built-in type <code>TYPE</code>. + * + * <p>The UNO type is not directly mapped to <code>java.lang.Class</code> for at + * least two reasons. For one, some UNO types (like <code>UNSIGNED + * SHORT</code>) 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.)</p> + * + * <p>A <code>Type</code> is uniquely determined by its type class (a + * <code>TypeClass</code>) and its type name (a <code>String</code>); these two + * will never be <code>null</code>. A <code>Type</code> may have an additional + * "z class" (a <code>java.lang.Class</code>), giving a Java class type that + * corresponds to the UNO type. Also, a <code>Type</code> can cache a type + * description (a <code>com.sun.star.uno.typedesc.TypeDescription</code>), which can be + * computed and set by <code>TypeDescription.getTypeDescription</code>. + */ +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 + // successfully: + + 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 class TypeInfo { + TypeInfo( + TypeClass thePrimary, TypeClass theAlternative, + boolean theSequenceComponentType) + { + primary = thePrimary; + alternative = theAlternative; + sequenceComponentType = theSequenceComponentType; + } + + final TypeClass primary; + final TypeClass alternative; + final boolean sequenceComponentType; + } + + private static final HashMap<Class<?>, TypeInfo> __javaClassToTypeClass = + new HashMap<Class<?>, TypeInfo>(); + static { + __javaClassToTypeClass.put( + void.class, new TypeInfo(TypeClass.VOID, TypeClass.VOID, false)); + __javaClassToTypeClass.put( + Void.class, new TypeInfo(TypeClass.VOID, TypeClass.VOID, false)); + __javaClassToTypeClass.put( + boolean.class, + new TypeInfo(TypeClass.BOOLEAN, TypeClass.BOOLEAN, true)); + __javaClassToTypeClass.put( + Boolean.class, + new TypeInfo(TypeClass.BOOLEAN, TypeClass.BOOLEAN, false)); + __javaClassToTypeClass.put( + byte.class, new TypeInfo(TypeClass.BYTE, TypeClass.BYTE, true)); + __javaClassToTypeClass.put( + Byte.class, new TypeInfo(TypeClass.BYTE, TypeClass.BYTE, false)); + __javaClassToTypeClass.put( + short.class, + new TypeInfo(TypeClass.SHORT, TypeClass.UNSIGNED_SHORT, true)); + __javaClassToTypeClass.put( + Short.class, + new TypeInfo(TypeClass.SHORT, TypeClass.UNSIGNED_SHORT, false)); + __javaClassToTypeClass.put( + int.class, + new TypeInfo(TypeClass.LONG, TypeClass.UNSIGNED_LONG, true)); + __javaClassToTypeClass.put( + Integer.class, + new TypeInfo(TypeClass.LONG, TypeClass.UNSIGNED_LONG, false)); + __javaClassToTypeClass.put( + long.class, + new TypeInfo(TypeClass.HYPER, TypeClass.UNSIGNED_HYPER, true)); + __javaClassToTypeClass.put( + Long.class, + new TypeInfo(TypeClass.HYPER, TypeClass.UNSIGNED_HYPER, false)); + __javaClassToTypeClass.put( + float.class, new TypeInfo(TypeClass.FLOAT, TypeClass.FLOAT, true)); + __javaClassToTypeClass.put( + Float.class, new TypeInfo(TypeClass.FLOAT, TypeClass.FLOAT, false)); + __javaClassToTypeClass.put( + double.class, + new TypeInfo(TypeClass.DOUBLE, TypeClass.DOUBLE, true)); + __javaClassToTypeClass.put( + Double.class, + new TypeInfo(TypeClass.DOUBLE, TypeClass.DOUBLE, false)); + __javaClassToTypeClass.put( + char.class, new TypeInfo(TypeClass.CHAR, TypeClass.CHAR, true)); + __javaClassToTypeClass.put( + Character.class, + new TypeInfo(TypeClass.CHAR, TypeClass.CHAR, false)); + __javaClassToTypeClass.put( + String.class, + new TypeInfo(TypeClass.STRING, TypeClass.STRING, true)); + __javaClassToTypeClass.put( + Type.class, new TypeInfo(TypeClass.TYPE, TypeClass.TYPE, true)); + __javaClassToTypeClass.put( + Any.class, new TypeInfo(TypeClass.ANY, TypeClass.ANY, true)); + __javaClassToTypeClass.put( + Object.class, + new TypeInfo(TypeClass.ANY, TypeClass.INTERFACE, true)); + } + + 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 <code>Type</code> which defaults to <code>VOID</code>. + */ + public Type() { + init(null, void.class, false, false, false); + } + + /** + * Constructs a new <code>Type</code> with the given type class and type + * name. + * + * @param typeName the type name. Must not be <code>null</code>. + * @param typeClass the type class. Must not be <code>null</code>, and must + * match the <code>typeName</code> (for example, it is illegal to + * combine a <code>typeName</code> of <code>"void"</code> with a + * <code>typeClass</code> of <code>BOOLEAN</code>). + */ + public Type(String typeName, TypeClass typeClass) { + _typeClass = typeClass; + _typeName = typeName; + } + + /** + * Constructs a new <code>Type</code> from the given + * <code>java.lang.Class</code>. + * + * <p>This is equivalent to <code>Type(zClass, false)</code>.</p> + * + * @param zClass the Java class of this type. Must not be + * <code>null</code>. + */ + public Type(Class<?> zClass) { + init(null, zClass, false, false, false); + } + + /** + * Constructs a new <code>Type</code> from the given + * <code>java.lang.Class</code>, handling ambiguous cases. + * + * <p>In certain cases, one Java class corresponds to two UNO types (e.g., + * the Java class <code>short[].class</code> corresponds to both a sequence + * of <code>SHORT</code> and a sequence of <code>UNSIGNED SHORT</code> in + * UNO). In such ambiguous cases, the parameter <code>alternative</code> + * controls which UNO type is chosen: + * <ul> + * <li>If the Java type is (an array type with element type) + * <code>short</code> or <code>java.lang.Short</code>: If + * <code>alternative</code> is <code>false</code>, the chosen UNO type is + * (a sequence type with element type) <code>SHORT</code>. If + * <code>alternative</code> is <code>true</code>, the chosen UNO type is + * (a sequence type with element type) <code>UNSIGNED SHORT</code>.</li> + * + * <li>If the Java type is (an array type with element type) + * <code>int</code> or <code>java.lang.Integer</code>: If + * <code>alternative</code> is <code>false</code>, the chosen UNO type is + * (a sequence type with element type) <code>LONG</code>. If + * <code>alternative</code> is <code>true</code>, the chosen UNO type is + * (a sequence type with element type) <code>UNSIGNED LONG</code>.</li> + * + * <li>If the Java type is (an array type with element type) + * <code>long</code> or <code>java.lang.Long</code>: If + * <code>alternative</code> is <code>false</code>, the chosen UNO type is + * (a sequence type with element type) <code>HYPER</code>. If + * <code>alternative</code> is <code>true</code>, the chosen UNO type is + * (a sequence type with element type) <code>UNSIGNED HYPER</code>.</li> + * + * <li>If the Java type is (an array type with element type) + * <code>java.lang.Object</code>: If <code>alternative</code> is + * <code>false</code>, the chosen UNO type is (a sequence type with + * element type) <code>ANY</code>. If <code>alternative</code> is + * <code>true</code>, the chosen UNO type is (a sequence type with element + * type) <code>com.sun.star.uno.XInterface</code>.</li> + * </ul> + * <p>In all other cases, the value of <code>alternative</code> is + * ignored.</p> + * + * <p>This constructor cannot be used to create <code>Type</code> instances + * that represent (sequences of) instantiated polymorphic struct types.</p> + * + * @param zClass the Java class of this type; must not be <code>null</code> + * @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, false); + } + + private Type( + Class<?> zClass, boolean alternative, boolean sequenceComponentType) + { + init(null, zClass, alternative, false, sequenceComponentType); + } + + /** + * Constructs a new <code>Type</code> from the given type description. + * + * <em>For internal URE use only. Not to be used by client code.</em> + * + * @param typeDescription a type description. Must not be + * <code>null</code>. + */ + public Type(TypeDescription typeDescription) { + _typeName = typeDescription.getTypeName(); + _typeClass = typeDescription.getTypeClass(); + _iTypeDescription = typeDescription; + } + + /** + * Constructs a new <code>Type</code> with the given type name. + * + * @param typeName the name of this type; must not be <code>null</code>. + */ + 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, false); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Constructs a new <code>Type</code> with the given type class. + * + * @param typeClass the type class of this type; must not be + * <code>null</code>. Only type classes for simple types are allowed + * here. + * + * @throws IllegalArgumentException if the given <code>typeClass</code> 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 <code>null</code>, but might be + * <code>UNKNOWN</code>. + */ + public TypeClass getTypeClass() { + return _typeClass; + } + + /** + * Gets the type name. + * + * @return the type name; will never be <code>null</code> + */ + public String getTypeName() { + return _typeName; + } + + /** + * Gets the Java class. + * + * @return the type name; may be <code>null</code> in extreme situations + * (inconsistent <code>TypeClass</code>, error loading a class) + */ + public Class<?> getZClass() { + synchronized (this) { + if (_class == null) { + _class = determineClass(); + } + } + return _class; + } + + /** + * Gives the type description of this type. + * + * <em>For internal URE use only. Not to be used by client code.</em> + * + * @return the type description; may be <code>null</code> + */ + public TypeDescription getTypeDescription() { + return _iTypeDescription; + } + + /** + * Sets the type description for this type. + * + * <em>For internal URE use only. Not to be used by client code.</em> + * + * @param typeDescription the type description + */ + public void setTypeDescription(TypeDescription 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); + } + // fall-through + 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; + } + } + + /** + * Indicates whether some other object is equal to this one. + * + * @param obj the reference object with which to compare. + * @return <code>true</code> if this object is the same as the obj argument; + * <code>false</code> otherwise. + * @see java.lang.Object#equals + */ + @Override + public boolean equals(Object obj) { + return obj instanceof Type + && _typeClass == ((Type) obj)._typeClass + && _typeName.equals(((Type) obj)._typeName); + } + + /** + * Returns a hash code value for the object. + * + * @return a hash code value for this object. + * @see java.lang.Object#hashCode + */ + @Override + public int hashCode() { + return _typeName.hashCode(); + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + * @see java.lang.Object#toString + */ + @Override + public String toString() { + return "Type[" + _typeName + "]"; + } + + private void init( + String name, Class<?> zClass, boolean alternative, boolean arguments, + boolean sequenceComponentType) + { + TypeInfo info = __javaClassToTypeClass.get(zClass); + if (info != null) { + if (sequenceComponentType && !info.sequenceComponentType) { + throw new IllegalArgumentException( + zClass + " cannot be sequence component type"); + } + // info only contains primitive type classes, except for + // TypeClass.INTERFACE, which stands for XInterface (the alternative + // interpretation of java.lang.Object): + _typeClass = alternative ? info.alternative : info.primary; + _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, true); + _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; + } + + private TypeClass _typeClass; // TODO should be final + private String _typeName; // TODO should be final + + private Class<?> _class; + private TypeDescription _iTypeDescription; +} diff --git a/ridljar/com/sun/star/uno/UnoRuntime.java b/ridljar/com/sun/star/uno/UnoRuntime.java new file mode 100644 index 000000000..1f011aa77 --- /dev/null +++ b/ridljar/com/sun/star/uno/UnoRuntime.java @@ -0,0 +1,726 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +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 java.util.UUID; +import java.util.WeakHashMap; + +import com.sun.star.lib.uno.typedesc.FieldDescription; +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. + * + * <p>The methods <code>queryInterface</code> and <code>areSame</code> delegate + * calls to the implementing objects and are used instead of casts, + * <code>instanceof</code>, <code>==</code>, and <code>equals</code>.</p> + * + * <p>For historic reasons, this class is not <code>final</code>, and has a + * <code>public</code> 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.</p> + * + * @see com.sun.star.uno.IBridge + * @see com.sun.star.uno.IEnvironment + * @see com.sun.star.uno.IQueryInterface + */ +public class UnoRuntime { + /** + * @deprecated As of UDK 3.2.0, do not create instances of this class. + * It is considered a historic mistake to have a <code>public</code> + * constructor for this class, which only has <code>static</code> members. + * Also, this class might be changed to become <code>final</code> in a + * future version. + */ + @Deprecated + public UnoRuntime() {} + + /** + * Generates a worldwide unique identifier string. + * + * <p>It is guaranteed that every invocation of this method generates a new + * ID, which is unique within the VM. The quality of “worldwide + * unique” will depend on the actual implementation, you should look + * at the source to determine if it meets your requirements.</p> + * + * @return a unique <code>String</code> + */ + 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 worldwide unique object identifier (OID) for the given + * Java object. + * + * <p>It is guaranteed that subsequent calls to this method with the same + * Java object will give the same ID.</p> + * + * <p>This method is generally of little use for client code. It should be + * considered a mistake that this method is published at all.</p> + * + * @param object any object for which an OID shall be generated; must not be + * <code>null</code> + * @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(); + } + if (oid == null) { + synchronized (oidMap) { + oid = oidMap.get(object); + if (oid == null) { + oid = UUID.randomUUID().toString() + oidSuffix; + oidMap.put(object, oid); + } + } + } + return oid; + } + + /** + * Queries the given UNO object for the given UNO interface type. + * + * <p>This method returns <code>null</code> in case the given UNO object + * does not support the given UNO interface type (or is itself + * <code>null</code>). 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.</p> + * + * @param type the requested UNO interface type; must be a <code>Type</code> + * object representing a UNO interface type + * @param object a reference to any Java object representing (a facet of) a + * UNO object; may be <code>null</code> + * @return a reference to the requested UNO interface type if available, + * otherwise <code>null</code> + * @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 <T> the requested 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 <code>null</code> + * @return a reference to the requested UNO interface type if available, + * otherwise <code>null</code> + * @see #queryInterface(Type, Object) + */ + @SuppressWarnings("unchecked") + public static <T> T queryInterface(Class<T> zInterface, Object object) { + return (T) queryInterface(new Type(zInterface), object); + } + + /** + Tests two UNO <code>ANY</code> values for equality. + + <p>Two UNO values are <dfn>equal</dfn> if and only if they are of the + same UNO type <var>t</var>, and they meet the following condition, + depending on <var>t</var>:</p> + <ul> + <li>If <var>t</var> is a primitive type, then both values must denote + the same element of the set of values of <var>t</var>.</li> + + <li>If <var>t</var> is a structured type, then both values must + recursively contain corresponding values that are equal.</li> + + <li>If <var>t</var> is an interface type, then the two values must be + either both null references, or both references to the same UNO + object.</li> + </ul> + + @param any1 a Java value representing a UNO <code>ANY</code> value. + + @param any2 a Java value representing a UNO <code>ANY</code> value. + + @return <code>true</code> 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: + FieldDescription[] fs; + try { + fs = TypeDescription.getTypeDescription(t). + getFieldDescriptions(); + } catch (ClassNotFoundException e) { + throw new java.lang.RuntimeException(e); + } + 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); + } + } + 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 <code>null</code> + value). + + <p>This is useful for members of parameterized type of instantiated + polymorphic struct types, as <code>null</code> is a valid value there + (and only there, for all types except <code>ANY</code> and interface + types).</p> + + @param type a non-void, non-exception UNO type. + + @param value a Java value representing a UNO value of the given UNO type, + or <code>null</code>. + + @return the given value, or the neutral value of the given type, if the + given value was an invalid <code>null</code> 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 Byte.valueOf((byte) 0); + case TypeClass.SHORT_value: + case TypeClass.UNSIGNED_SHORT_value: + return Short.valueOf((short) 0); + case TypeClass.LONG_value: + case TypeClass.UNSIGNED_LONG_value: + return Integer.valueOf(0); + case TypeClass.HYPER_value: + case TypeClass.UNSIGNED_HYPER_value: + return Long.valueOf(0); + 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((Class[]) null). + newInstance((Object[]) null); + } catch (java.lang.RuntimeException e) { + throw e; + } catch (java.lang.Exception e) { + throw new java.lang.RuntimeException(e); + } + case TypeClass.ENUM_value: + try { + return type.getZClass().getMethod("getDefault", (Class[]) null). + invoke(null, (Object[]) null); + } catch (java.lang.RuntimeException e) { + throw e; + } catch (java.lang.Exception e) { + throw new java.lang.RuntimeException(e); + } + 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 <code>null</code> if + * no context has been set for the current thread. + * + * <p>The current context is thread local, which means that this method + * returns the context that was last set for this thread.</p> + * + * @return the current context of the current thread, or <code>null</code> + * if no context has been set for the current thread + */ + public static XCurrentContext getCurrentContext() { + return currentContext.get(); + } + + /** + * Sets the current context for the current thread. + * + * <p>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 <code>finally</code> + * block).</p> + * + * @param context the context to be set; if <code>null</code>, any + * previously set context will be removed + */ + public static void setCurrentContext(XCurrentContext context) { + if (context == null) { + currentContext.remove(); + } else { + currentContext.set(context); + } + } + + /** + * Retrieves an environment of type <code>name</code> with context + * <code>context</code>. + * + * <p>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 + * <code>com.sun.star.lib.uno.environments.<var>name</var>.<!-- + * --><var>name</var>_environment</code>.</p> + * + * @param name the name of the environment + * @param context the context of the environment + * @throws Exception if something goes awry. + * @return an environment. + * @see com.sun.star.uno.IEnvironment + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IEnvironment getEnvironment(String name, Object context) + throws java.lang.Exception + { + synchronized (environments) { + IEnvironment env = WeakMap.getValue( + environments.get(name + context)); + if (env == null) { + Class<?> c = Class.forName( + "com.sun.star.lib.uno.environments." + name + "." + name + + "_environment"); + Constructor<?> ctor = c.getConstructor( + new Class[] { Object.class }); + env = (IEnvironment) ctor.newInstance(new Object[] { context }); + environments.put(name + context, env); + } + return env; + } + } + + /** + * Gets a bridge from environment <code>from</code> to environment + * <code>to</code>. + * + * <p>Creates a new bridge, if the requested bridge does not yet exist, and + * hands the arguments to the bridge.</p> + * + * <p>If the requested bridge does not exist, it is searched for in package + * <code>com.sun.star.lib.uno.bridges.<var>from</var>_<var>to</var>;</code> + * and the root classpath as + * <code><var>from</var>_<var>to</var>_bridge</code>.</p> + * + * @param from the source environment + * @param to the target environment + * @param args the initial arguments for the bridge + * @throws Exception if something goes awry. + * @return the requested bridge + * @see #getBridgeByName + * @see com.sun.star.uno.IBridge + * @see com.sun.star.uno.IEnvironment + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IBridge getBridge( + IEnvironment from, IEnvironment to, Object[] args) + throws java.lang.Exception + { + synchronized (bridges) { + String name = from.getName() + "_" + to.getName(); + String hashName = from.getName() + from.getContext() + "_" + + to.getName() + to.getContext(); + IBridge bridge = WeakMap.getValue(bridges.get(hashName)); + if(bridge == null) { + Class<?> zClass = null; + String className = name + "_bridge"; + try { + zClass = Class.forName(className); + } catch (ClassNotFoundException e) { + className = "com.sun.star.lib.uno.bridges." + name + "." + + className; + zClass = Class.forName(className); + } + Class<?>[] signature = { + IEnvironment.class, IEnvironment.class, args.getClass() }; + Constructor<?> constructor = zClass.getConstructor(signature); + Object[] iargs = { from, to, args }; + bridge = (IBridge) constructor.newInstance(iargs); + bridges.put(hashName, bridge); + } + return bridge; + } + } + + /** + * Gets a bridge from environment <code>from</code> to environment + * <code>to</code>. + * + * <p>Creates a new bridge, if the requested bridge does not yet exist, and + * hands the arguments to the bridge.</p> + * + * <p>If the requested bridge does not exist, it is searched for in package + * <code>com.sun.star.lib.uno.bridges.<var>from</var>_<var>to</var>;</code> + * and the root classpath as + * <code><var>from</var>_<var>to</var>_bridge</code>. The used environments + * are retrieved through <code>getEnvironment</code>.</p> + * + * @param from the name of the source environment + * @param fromContext the context for the source environment + * @param to the name of the target environment + * @param toContext the context for the target environment + * @param args the initial arguments for the bridge + * @throws Exception if something goes awry. + * @return the requested bridge + * @see #getBridge + * @see #getEnvironment + * @see com.sun.star.uno.IBridge + * @see com.sun.star.uno.IEnvironment + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IBridge getBridgeByName( + String from, Object fromContext, String to, Object toContext, + Object[] args) throws java.lang.Exception + { + return getBridge( + getEnvironment(from, fromContext), getEnvironment(to, toContext), + args); + } + + /** + * Returns an array of all active bridges. + * + * @return an array of <code>IBridge</code> objects + * @see com.sun.star.uno.IBridge + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IBridge[] getBridges() { + ArrayList<Object> l = new ArrayList<Object>(); + synchronized (bridges) { + for (Iterator<java.lang.ref.WeakReference<IBridge>> i = bridges.values().iterator(); i.hasNext();) { + IBridge o = WeakMap.getValue(i.next()); + if (o != null) { + l.add(o); + } + } + } + return l.toArray(new IBridge[l.size()]); + } + + /** + * Gets a mapping from environment <code>from</code> to environment + * <code>to</code>. + * + * <p>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.</p> + * + * @param from the source environment + * @param to the target environment + * @throws Exception if something goes awry. + * @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. + */ + @Deprecated + 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 environment <code>from</code> to environment + * <code>to</code>. + * + * <p>The used environments are retrieved through + * <code>getEnvironment</code>.</p> + * + * @param from the name of the source environment + * @param to the name of the target environment + * @throws Exception if something goes awry. + * @return the requested mapping + * @see #getEnvironment + * @see #getMapping + * @see com.sun.star.uno.IMapping + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static IMapping getMappingByName(String from, String to) + throws java.lang.Exception + { + return getMapping(getEnvironment(from, null), getEnvironment(to, null)); + } + + /** + * Resets this <code>UnoRuntime</code> to its initial state. + * + * <p>Releases all references to bridges and environments.</p> + * + * @return true if another thread didn't re-insert some bridge or + * environment before the method returns. Why that information + * would be useful is anybody's guess. + * + * @deprecated As of UDK 3.2.0, this method is deprecated, without + * offering a replacement. + */ + @Deprecated + public static boolean reset() { + synchronized (bridges) { + for (Iterator<java.lang.ref.WeakReference<IBridge>> i = bridges.values().iterator(); i.hasNext();) { + IBridge b = 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(); + Thread.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. + */ + @Deprecated + public static 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 WeakHashMap<Object,String> oidMap = new WeakHashMap<Object,String>(); + 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<XCurrentContext> currentContext = new ThreadLocal<XCurrentContext>(); + + private static final WeakMap<String,IEnvironment> environments = new WeakMap<String,IEnvironment>(); + private static final WeakMap<String,IBridge> bridges = new WeakMap<String,IBridge>(); +} diff --git a/ridljar/com/sun/star/uno/WeakReference.java b/ridljar/com/sun/star/uno/WeakReference.java new file mode 100644 index 000000000..a6b171ac2 --- /dev/null +++ b/ridljar/com/sun/star/uno/WeakReference.java @@ -0,0 +1,154 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package com.sun.star.uno; + +/** + * This class holds weak reference to an object. + * + * <p>It actually holds a reference to a <code>com.sun.star.XAdapter</code> + * implementation and obtains a hard reference if necessary. + */ +public class WeakReference +{ + private OWeakRefListener m_listener; + // There is no default constructor. Every instance must register itself with the + // XAdapter interface, which is done in the constructors. Assume we have this code + // WeakReference ref= new WeakReference(); + // ref = someOtherWeakReference; + // + // ref would not be notified (XReference.dispose()) because it did not register + // itself. Therefore the XAdapter would be kept alive although this is not + // necessary. + + /** + * Creates an instance of this class. + * + * @param obj another instance that is to be copied. + */ + public WeakReference(WeakReference obj) + { + if (obj == null) { + return; + } + Object weakImpl = obj.get(); + if (weakImpl == null) { + return; + } + XWeak weak = UnoRuntime.queryInterface(XWeak.class, weakImpl); + if (weak != null) { + XAdapter adapter = weak.queryAdapter(); + if (adapter != null) + m_listener = new OWeakRefListener(adapter); + } + } + + /** + * Creates an instance of this class. + * + * @param obj XWeak implementation. + */ + public WeakReference(Object obj) + { + XWeak weak= UnoRuntime.queryInterface(XWeak.class, obj); + if (weak != null) + { + XAdapter adapter= weak.queryAdapter(); + if (adapter != null) + m_listener= new OWeakRefListener(adapter); + } + } + + /** + * Returns a hard reference to the object that is kept weak by this class. + * + * @return a hard reference to the XWeak implementation. + */ + public Object get() + { + if (m_listener != null) + return m_listener.get(); + return null; + } +} + +/** + * Implementation of com.sun.star.uno.XReference for use with WeakReference. + * + * <p>It keeps the XAdapter implementation and registers always with it. + * Deregistering occurs on notification by the adapter and the adapter is + * released.</p> + */ +class OWeakRefListener implements XReference +{ + private XAdapter m_adapter; + + /** + * The constructor registered this object with adapter. + * + * @param adapter the XAdapter implementation. + */ + OWeakRefListener( XAdapter adapter) + { + m_adapter= adapter; + m_adapter.addReference(this); + } + + /** + * Method of <code>com.sun.star.uno.XReference</code>. + * + * <p>When called, it deregisters this object with the adapter and releases + * the reference to it.</p> + */ + synchronized public void dispose() + { + if (m_adapter != null) + { + m_adapter.removeReference(this); + m_adapter= null; + } + } + + /** + * Obtains a hard reference to the object which is kept weak by the adapter + * and returns it. + * + * @return hard reference to the otherwise weakly kept object. + */ + synchronized Object get() + { + Object retVal= null; + if (m_adapter != null) + { + retVal= m_adapter.queryAdapted(); + if (retVal == null) + { + // If this object registered as listener with XAdapter while it was notifying + // the listeners then this object might not have been notified. If queryAdapted + // returned null then the weak kept object is dead and the listeners have already + // been notified. And we missed it. + m_adapter.removeReference(this); + m_adapter= null; + } + } + return retVal; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |