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 /scripting/java/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 'scripting/java/com')
46 files changed, 8668 insertions, 0 deletions
diff --git a/scripting/java/com/sun/star/script/framework/browse/DialogFactory.java b/scripting/java/com/sun/star/script/framework/browse/DialogFactory.java new file mode 100644 index 000000000..08cfba4c8 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/browse/DialogFactory.java @@ -0,0 +1,255 @@ +/* + * 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.script.framework.browse; + +import com.sun.star.awt.XButton; +import com.sun.star.awt.XControl; +import com.sun.star.awt.XControlContainer; +import com.sun.star.awt.XControlModel; +import com.sun.star.awt.XDialog; +import com.sun.star.awt.XTextComponent; +import com.sun.star.awt.XToolkit; +import com.sun.star.awt.XWindow; + +import com.sun.star.beans.XPropertySet; + +import com.sun.star.container.XNameContainer; + +import com.sun.star.lang.EventObject; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +public class DialogFactory { + + private static DialogFactory factory; + private final XComponentContext xComponentContext; + + // singleton + private DialogFactory(XComponentContext xComponentContext) { + this.xComponentContext = xComponentContext; + } + + public static void createDialogFactory(XComponentContext xComponentContext) { + synchronized (DialogFactory.class) { + if (factory == null) { + factory = new DialogFactory(xComponentContext); + } + } + } + + public static DialogFactory getDialogFactory() throws java.lang.Exception { + if (factory == null) { + throw new java.lang.Exception("DialogFactory not initialized"); + } + return factory; + } + + public String showInputDialog(String title, String prompt) { + final XDialog xDialog; + + try { + xDialog = createInputDialog(title, prompt); + } catch (com.sun.star.uno.Exception e) { + return null; + } + + // add an action listener to the button controls + XControlContainer controls = + UnoRuntime.queryInterface(XControlContainer.class, xDialog); + + XButton okButton = + UnoRuntime.queryInterface(XButton.class, controls.getControl("Ok")); + + okButton.setActionCommand("Ok"); + + XButton cancelButton = + UnoRuntime.queryInterface(XButton.class, controls.getControl("Cancel")); + + cancelButton.setActionCommand("Cancel"); + + final XTextComponent textField = + UnoRuntime.queryInterface(XTextComponent.class, + controls.getControl("NameField")); + + final ResultHolder resultHolder = new ResultHolder(); + + com.sun.star.awt.XActionListener listener = + new com.sun.star.awt.XActionListener() { + public void actionPerformed(com.sun.star.awt.ActionEvent e) { + if (e.ActionCommand.equals("Cancel")) { + resultHolder.setResult(null); + xDialog.endExecute(); + } else { + resultHolder.setResult(textField.getText()); + xDialog.endExecute(); + } + } + + public void disposing(EventObject o) { + // does nothing + } + }; + + okButton.addActionListener(listener); + cancelButton.addActionListener(listener); + + xDialog.execute(); + + return (String)resultHolder.getResult(); + } + + private void setDimensions(Object o, int x, int y, int width, int height) throws + com.sun.star.uno.Exception { + + XPropertySet props = UnoRuntime.queryInterface(XPropertySet.class, o); + + props.setPropertyValue("PositionX", Integer.valueOf(x)); + props.setPropertyValue("PositionY", Integer.valueOf(y)); + props.setPropertyValue("Height", Integer.valueOf(height)); + props.setPropertyValue("Width", Integer.valueOf(width)); + } + + private XDialog createInputDialog(String title, String prompt) throws + com.sun.star.uno.Exception { + + if (title == null || title.length() == 0) { + title = "Scripting Framework"; + } + + if (prompt == null || prompt.length() == 0) { + prompt = "Enter name"; + } + + // get the service manager from the component context + XMultiComponentFactory xMultiComponentFactory = + xComponentContext.getServiceManager(); + + // create the dialog model and set the properties + Object dialogModel = xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.awt.UnoControlDialogModel", + xComponentContext); + + setDimensions(dialogModel, 100, 100, 157, 58); + + XPropertySet props = + UnoRuntime.queryInterface(XPropertySet.class, dialogModel); + + props.setPropertyValue("Title", title); + + // get the service manager from the dialog model + XMultiServiceFactory xMultiServiceFactory = + UnoRuntime.queryInterface(XMultiServiceFactory.class, dialogModel); + + // create the label model and set the properties + Object label = xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlFixedTextModel"); + + setDimensions(label, 15, 5, 134, 12); + + XPropertySet labelProps = + UnoRuntime.queryInterface(XPropertySet.class, label); + + labelProps.setPropertyValue("Name", "PromptLabel"); + labelProps.setPropertyValue("Label", prompt); + + // create the text field + Object edit = xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlEditModel"); + + setDimensions(edit, 15, 18, 134, 12); + + XPropertySet editProps = + UnoRuntime.queryInterface(XPropertySet.class, edit); + + editProps.setPropertyValue("Name", "NameField"); + + // create the OK button + Object okButtonModel = xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlButtonModel"); + + setDimensions(okButtonModel, 40, 39, 38, 15); + + XPropertySet buttonProps = + UnoRuntime.queryInterface(XPropertySet.class, okButtonModel); + + buttonProps.setPropertyValue("Name", "Ok"); + buttonProps.setPropertyValue("Label", "Ok"); + + // create the Cancel button + Object cancelButtonModel = xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlButtonModel"); + + setDimensions(cancelButtonModel, 83, 39, 38, 15); + + buttonProps = + UnoRuntime.queryInterface(XPropertySet.class, cancelButtonModel); + + buttonProps.setPropertyValue("Name", "Cancel"); + buttonProps.setPropertyValue("Label", "Cancel"); + + // insert the control models into the dialog model + XNameContainer xNameCont = + UnoRuntime.queryInterface(XNameContainer.class, dialogModel); + + xNameCont.insertByName("PromptLabel", label); + xNameCont.insertByName("NameField", edit); + xNameCont.insertByName("Ok", okButtonModel); + xNameCont.insertByName("Cancel", cancelButtonModel); + + // create the dialog control and set the model + Object dialog = xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.awt.UnoControlDialog", + xComponentContext); + + XControl xControl = UnoRuntime.queryInterface(XControl.class, dialog); + + XControlModel xControlModel = + UnoRuntime.queryInterface(XControlModel.class, dialogModel); + + xControl.setModel(xControlModel); + + // create a peer + Object toolkit = xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.awt.ExtToolkit", + xComponentContext); + + XToolkit xToolkit = UnoRuntime.queryInterface(XToolkit.class, toolkit); + XWindow xWindow = UnoRuntime.queryInterface(XWindow.class, xControl); + xWindow.setVisible(false); + xControl.createPeer(xToolkit, null); + + return UnoRuntime.queryInterface(XDialog.class, dialog); + } + + private static class ResultHolder { + + private Object result = null; + + public Object getResult() { + return result; + } + + public void setResult(Object result) { + this.result = result; + } + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode.java b/scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode.java new file mode 100644 index 000000000..cdb8715a5 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode.java @@ -0,0 +1,309 @@ +/* + * 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.script.framework.browse; + +import com.sun.star.beans.XIntrospectionAccess; + +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lib.uno.helper.PropertySet; + +import com.sun.star.script.XInvocation; +import com.sun.star.script.browse.BrowseNodeTypes; +import com.sun.star.script.browse.XBrowseNode; +import com.sun.star.script.framework.container.Parcel; +import com.sun.star.script.framework.container.ParcelContainer; +import com.sun.star.script.framework.container.ScriptEntry; +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptProvider; + +import com.sun.star.ucb.XSimpleFileAccess; + +import com.sun.star.uno.Any; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.swing.JOptionPane; + +public class ParcelBrowseNode extends PropertySet implements + XBrowseNode, XInvocation { + + private final ScriptProvider provider; + private Collection<XBrowseNode> browsenodes; + private final ParcelContainer container; + private Parcel parcel; + // these four are properties, they are accessed via reflection + public boolean deletable = true; + public boolean editable = false; + public boolean creatable = false; + public boolean renamable = true; + + public ParcelBrowseNode(ScriptProvider provider, ParcelContainer container, + String parcelName) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + this.provider = provider; + this.container = container; + + this.parcel = (Parcel)this.container.getByName(parcelName); + + registerProperty("Deletable", new Type(boolean.class), (short)0, "deletable"); + registerProperty("Editable", new Type(boolean.class), (short)0, "editable"); + registerProperty("Creatable", new Type(boolean.class), (short)0, "creatable"); + registerProperty("Renamable", new Type(boolean.class), (short)0, "renamable"); + if (provider.hasScriptEditor()) + { + this.creatable = true; + } + + String parcelDirUrl = parcel.getPathToParcel(); + XComponentContext xCtx = provider.getScriptingContext().getComponentContext(); + XMultiComponentFactory xFac = xCtx.getServiceManager(); + + try { + XSimpleFileAccess xSFA = UnoRuntime.queryInterface(XSimpleFileAccess.class, + xFac.createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", + xCtx)); + if ( xSFA != null && ( xSFA.isReadOnly( parcelDirUrl ) || + container.isUnoPkg() ) ) + { + deletable = false; + editable = false; + creatable = false; + renamable = false; + } + } catch (com.sun.star.uno.Exception e) { + // TODO propagate potential errors + LogUtils.DEBUG("Caught exception creating ParcelBrowseNode " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + + } + + public String getName() { + return parcel.getName(); + } + + public XBrowseNode[] getChildNodes() { + try { + + if (hasChildNodes()) { + String[] names = parcel.getElementNames(); + browsenodes = new ArrayList<XBrowseNode>(names.length); + + for (String name : names) { + browsenodes.add(new ScriptBrowseNode(provider, parcel, name)); + } + } else { + LogUtils.DEBUG("ParcelBrowseNode.getChildeNodes no children "); + return new XBrowseNode[0]; + } + } catch (Exception e) { + LogUtils.DEBUG("Failed to getChildeNodes, exception: " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + return new XBrowseNode[0]; + } + + return browsenodes.toArray(new XBrowseNode[browsenodes.size()]); + } + + public boolean hasChildNodes() { + if (container != null && parcel != null && container.hasByName(getName())) { + return parcel.hasElements(); + } + + return false; + } + + public short getType() { + return BrowseNodeTypes.CONTAINER; + } + + @Override + public String toString() { + return getName(); + } + + // implementation of XInvocation interface + public XIntrospectionAccess getIntrospection() { + return null; + } + + public Object invoke(String aFunctionName, Object[] aParams, + short[][] aOutParamIndex, Object[][] aOutParam) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + + LogUtils.DEBUG("ParcelBrowseNode invoke for " + aFunctionName); + + // Initialise the out parameters - not used but prevents error in + // UNO bridge + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + Any result = new Any(new Type(Boolean.class), Boolean.TRUE); + + if (aFunctionName.equals("Creatable")) { + try { + String newName; + + if (aParams == null || aParams.length < 1 + || !AnyConverter.isString(aParams[0])) { + + String prompt = "Enter name for new Script"; + String title = "Create Script"; + + // try to get a DialogFactory instance, if it fails + // just use a Swing JOptionPane to prompt for the name + try { + DialogFactory dialogFactory = DialogFactory.getDialogFactory(); + newName = dialogFactory.showInputDialog(title, prompt); + } catch (Exception e) { + + newName = JOptionPane.showInputDialog(null, prompt, title, + JOptionPane.QUESTION_MESSAGE); + + } + } else { + newName = AnyConverter.toString(aParams[0]); + } + + if (newName == null || newName.length() == 0) { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } else { + String source = provider.getScriptEditor().getTemplate(); + + String languageName = + newName + "." + provider.getScriptEditor().getExtension(); + + String language = container.getLanguage(); + + ScriptEntry entry = new ScriptEntry(language, languageName); + + Parcel parcel = (Parcel)container.getByName(getName()); + ScriptMetaData data = new ScriptMetaData(parcel, entry, source); + parcel.insertByName(languageName, data); + + ScriptBrowseNode sbn = + new ScriptBrowseNode(provider, parcel, languageName); + + if (browsenodes == null) { + LogUtils.DEBUG("browsenodes null!!"); + browsenodes = new ArrayList<XBrowseNode>(4); + } + + browsenodes.add(sbn); + result = new Any(new Type(XBrowseNode.class), sbn); + } + } catch (Exception e) { + LogUtils.DEBUG("ParcelBrowseNode[create] failed with: " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } + } else if (aFunctionName.equals("Deletable")) { + try { + if (container.deleteParcel(getName())) { + result = new Any(new Type(Boolean.class), Boolean.TRUE); + } else { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } + } catch (Exception e) { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } + } else if (aFunctionName.equals("Renamable")) { + + String newName = null; + + try { + + if (aParams == null || aParams.length < 1 || + !AnyConverter.isString(aParams[0])) { + String prompt = "Enter new name for Library"; + String title = "Rename Library"; + + // try to get a DialogFactory instance, if it fails + // just use a Swing JOptionPane to prompt for the name + try { + DialogFactory dialogFactory = DialogFactory.getDialogFactory(); + newName = dialogFactory.showInputDialog(title, prompt); + } catch (Exception e) { + + newName = JOptionPane.showInputDialog(null, prompt, title, + JOptionPane.QUESTION_MESSAGE); + + } + } else { + newName = AnyConverter.toString(aParams[0]); + } + + container.renameParcel(getName(), newName); + Parcel p = (Parcel)container.getByName(newName); + + if (browsenodes == null) { + getChildNodes(); + } + + ScriptBrowseNode[] childNodes = + browsenodes.toArray(new ScriptBrowseNode[browsenodes.size()]); + + for (int index = 0; index < childNodes.length; index++) { + childNodes[ index ].updateURI(p); + } + + result = new Any(new Type(XBrowseNode.class), this); + } catch (Exception e) { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } + } + + else { + throw new com.sun.star.lang.IllegalArgumentException( + "Function " + aFunctionName + " not supported."); + } + + return result; + } + + public void setValue(String aPropertyName, Object aValue) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + } + + public Object getValue(String aPropertyName) throws + com.sun.star.beans.UnknownPropertyException { + return null; + } + + public boolean hasMethod(String aName) { + return false; + } + + public boolean hasProperty(String aName) { + return false; + } +} diff --git a/scripting/java/com/sun/star/script/framework/browse/PkgProviderBrowseNode.java b/scripting/java/com/sun/star/script/framework/browse/PkgProviderBrowseNode.java new file mode 100644 index 000000000..6cdfd2a77 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/browse/PkgProviderBrowseNode.java @@ -0,0 +1,49 @@ +/* + * 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.script.framework.browse; + +import com.sun.star.script.framework.container.ParcelContainer; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptProvider; + +import com.sun.star.uno.XComponentContext; + +public class PkgProviderBrowseNode extends ProviderBrowseNode { + + public PkgProviderBrowseNode(ScriptProvider provider, ParcelContainer container, + XComponentContext xCtx) { + + super(provider, container, xCtx); + + LogUtils.DEBUG("*** PkgProviderBrowseNode ctor container name = " + + container.getName()); + LogUtils.DEBUG("*** PkgProviderBrowseNode ctor container path = " + + container.getParcelContainerDir()); + LogUtils.DEBUG("*** PkgProviderBrowseNode ctor, container has num parcels = " + + container.getElementNames().length); + deletable = false; + editable = false; + creatable = false; + } + + @Override public String getName() { + return (container != null) ? container.getName() : "Unknown"; + } + +} diff --git a/scripting/java/com/sun/star/script/framework/browse/ProviderBrowseNode.java b/scripting/java/com/sun/star/script/framework/browse/ProviderBrowseNode.java new file mode 100644 index 000000000..91c2566be --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/browse/ProviderBrowseNode.java @@ -0,0 +1,264 @@ +/* + * 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.script.framework.browse; + +import com.sun.star.beans.XIntrospectionAccess; + +import com.sun.star.lang.XMultiComponentFactory; + +import com.sun.star.lib.uno.helper.PropertySet; + +import com.sun.star.script.XInvocation; +import com.sun.star.script.browse.BrowseNodeTypes; +import com.sun.star.script.browse.XBrowseNode; +import com.sun.star.script.framework.container.ParcelContainer; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptProvider; + +import com.sun.star.ucb.XSimpleFileAccess; + +import com.sun.star.uno.Any; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.swing.JOptionPane; + +public class ProviderBrowseNode extends PropertySet implements + XBrowseNode, XInvocation { + + private final ScriptProvider provider; + private Collection<XBrowseNode> browsenodes; + private final String name; + protected ParcelContainer container; + private final XComponentContext m_xCtx; + // these are properties, they are accessed by reflection + public boolean deletable = true; + public boolean creatable = true; + public boolean editable = false; + + public ProviderBrowseNode(ScriptProvider provider, ParcelContainer container, + XComponentContext xCtx) { + + LogUtils.DEBUG("*** ProviderBrowseNode ctor"); + this.container = container; + this.name = this.container.getLanguage(); + this.provider = provider; + this.m_xCtx = xCtx; + + registerProperty("Deletable", new Type(boolean.class), (short)0, "deletable"); + registerProperty("Creatable", new Type(boolean.class), (short)0, "creatable"); + registerProperty("Editable", new Type(boolean.class), (short)0, "editable"); + XMultiComponentFactory xFac = m_xCtx.getServiceManager(); + + try { + XSimpleFileAccess xSFA = UnoRuntime.queryInterface(XSimpleFileAccess.class, + xFac.createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", + xCtx)); + if ( container.isUnoPkg() || xSFA.isReadOnly( container.getParcelContainerDir() ) ) + { + deletable = false; + creatable = false; + } + } + // TODO propagate errors + catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG("Caught exception in creation of ProviderBrowseNode "); + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + } + + public String getName() { + return name; + } + + public XBrowseNode[] getChildNodes() { + LogUtils.DEBUG("***** ProviderBrowseNode.getChildNodes()"); + + if (hasChildNodes()) { + + // needs initialisation? + LogUtils.DEBUG("** ProviderBrowseNode.getChildNodes(), container is " + + container); + + String[] parcels = container.getElementNames(); + browsenodes = new ArrayList<XBrowseNode>(parcels.length); + + for (String parcel : parcels) { + try { + XBrowseNode node = new ParcelBrowseNode(provider, container, parcel); + browsenodes.add(node); + } catch (Exception e) { + LogUtils.DEBUG("*** Failed to create parcel node for " + parcel); + LogUtils.DEBUG(e.toString()); + } + } + + ParcelContainer[] packageContainers = container.getChildContainers(); + + LogUtils.DEBUG("**** For container named " + container.getName() + + " with root path " + container.getParcelContainerDir() + + " has " + packageContainers.length + " child containers "); + + for (ParcelContainer packageContainer : packageContainers) { + + XBrowseNode node = + new PkgProviderBrowseNode(provider, packageContainer, m_xCtx); + + browsenodes.add(node); + } + } else { + LogUtils.DEBUG("*** No container available"); + return new XBrowseNode[0]; + } + + return browsenodes.toArray(new XBrowseNode[browsenodes.size()]); + } + + public boolean hasChildNodes() { + boolean result = true; + + if (container == null || + (!container.hasElements() && container.getChildContainers().length == 0)) { + + result = false; + } + + if (container == null) { + LogUtils.DEBUG("***** ProviderBrowseNode.hasChildNodes(): " + "name=" + name + + ", path=<none>, result=" + result); + } else { + LogUtils.DEBUG("***** ProviderBrowseNode.hasChildNodes(): " + "name=" + name + + ", path=" + container.getParcelContainerDir() + ", result=" + result); + } + + return result; + } + + public short getType() { + return BrowseNodeTypes.CONTAINER; + } + + @Override + public String toString() { + return getName(); + } + + // implementation of XInvocation interface + public XIntrospectionAccess getIntrospection() { + return null; + } + + public Object invoke(String aFunctionName, Object[] aParams, + short[][] aOutParamIndex, Object[][] aOutParam) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + + // Initialise the out parameters - not used but prevents error in + // UNO bridge + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + Any result = new Any(new Type(Boolean.class), Boolean.TRUE); + + if (aFunctionName.equals("Creatable")) { + try { + String name; + + if (aParams == null || aParams.length < 1 || + !AnyConverter.isString(aParams[0])) { + + String prompt = "Enter name for new Parcel"; + String title = "Create Parcel"; + + // try to get a DialogFactory instance, if it fails + // just use a Swing JOptionPane to prompt for the name + try { + DialogFactory dialogFactory = DialogFactory.getDialogFactory(); + name = dialogFactory.showInputDialog(title, prompt); + } catch (Exception e) { + + name = JOptionPane.showInputDialog(null, prompt, title, + JOptionPane.QUESTION_MESSAGE); + + } + } else { + name = AnyConverter.toString(aParams[0]); + } + + if (name == null || name.length() == 0) { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } else { + + Object newParcel = container.createParcel(name); + LogUtils.DEBUG("Parcel created " + name + " " + newParcel); + + if (newParcel == null) { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } else { + ParcelBrowseNode parcel = new ParcelBrowseNode(provider, container, name); + LogUtils.DEBUG("created parcel node "); + + if (browsenodes == null) { + browsenodes = new ArrayList<XBrowseNode>(5); + } + + browsenodes.add(parcel); + result = new Any(new Type(XBrowseNode.class), parcel); + } + } + } catch (Exception e) { + LogUtils.DEBUG("ProviderBrowseNode[create] failed with: " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } + } else { + throw new com.sun.star.lang.IllegalArgumentException( + "Function " + aFunctionName + " not supported."); + } + + return result; + } + + public void setValue(String aPropertyName, Object aValue) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + } + + public Object getValue(String aPropertyName) throws + com.sun.star.beans.UnknownPropertyException { + + return null; + } + + public boolean hasMethod(String aName) { + return false; + } + + public boolean hasProperty(String aName) { + return false; + } +} diff --git a/scripting/java/com/sun/star/script/framework/browse/ScriptBrowseNode.java b/scripting/java/com/sun/star/script/framework/browse/ScriptBrowseNode.java new file mode 100644 index 000000000..5f91b1a78 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/browse/ScriptBrowseNode.java @@ -0,0 +1,300 @@ +/* + * 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.script.framework.browse; + +import com.sun.star.beans.XIntrospectionAccess; + +import com.sun.star.container.ElementExistException; +import com.sun.star.container.NoSuchElementException; + +import com.sun.star.lang.NoSupportException; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XMultiComponentFactory; + +import com.sun.star.lib.uno.helper.PropertySet; + +import com.sun.star.reflection.InvocationTargetException; + +import com.sun.star.script.XInvocation; +import com.sun.star.script.browse.BrowseNodeTypes; +import com.sun.star.script.browse.XBrowseNode; +import com.sun.star.script.framework.container.Parcel; +import com.sun.star.script.framework.container.ScriptEntry; +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptProvider; +import com.sun.star.script.provider.XScriptContext; + +import com.sun.star.ucb.XSimpleFileAccess; + +import com.sun.star.uno.Any; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +public class ScriptBrowseNode extends PropertySet implements + XBrowseNode, XInvocation { + + private final ScriptProvider provider; + + private Parcel parent; + private String name; + + // these are properties, accessed by reflection + public String uri; + public String description; + public boolean editable; + public boolean deletable = false; + public boolean renamable = false; + + public ScriptBrowseNode(ScriptProvider provider, Parcel parent, String name) { + + this.provider = provider; + this.name = name; + this.parent = parent; + ScriptMetaData data = null; + XComponentContext xCtx = provider.getScriptingContext().getComponentContext(); + XMultiComponentFactory xFac = xCtx.getServiceManager(); + + try { + data = parent.getByName( name ); + XSimpleFileAccess xSFA = UnoRuntime.queryInterface( + XSimpleFileAccess.class, + xFac.createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", + xCtx)); + + uri = data.getShortFormScriptURL(); + description = data.getDescription(); + if (provider.hasScriptEditor()) { + this.editable = true; + + try { + if (!parent.isUnoPkg() + && !xSFA.isReadOnly(parent.getPathToParcel())) { + + this.deletable = true; + this.renamable = true; + + } + } + // TODO propagate errors + catch (Exception e) { + LogUtils.DEBUG("Caught exception in creation of ScriptBrowseNode"); + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + + } + + } + // TODO fix exception types to be caught here, should we rethrow? + catch (Exception e) { + + LogUtils.DEBUG("** caught exception getting script data for " + name + + " ->" + e.toString()); + + } + + registerProperty("Deletable", new Type(boolean.class), (short)0, "deletable"); + registerProperty("Editable", new Type(boolean.class), (short)0, "editable"); + registerProperty("Renamable", new Type(boolean.class), (short)0, "renamable"); + registerProperty("URI", new Type(String.class), (short)0, "uri"); + registerProperty("Description", new Type(String.class), (short)0, + "description"); + } + + public String getName() { + return name; + } + + public XBrowseNode[] getChildNodes() { + return new XBrowseNode[0]; + } + + public boolean hasChildNodes() { + return false; + } + + public short getType() { + return BrowseNodeTypes.SCRIPT; + } + + @Override + public String toString() { + return getName(); + } + + public void updateURI(Parcel p) { + parent = p; + + try { + ScriptMetaData data = parent.getByName(name); + uri = data.getShortFormScriptURL(); + } + // TODO fix exception types to be caught here, should we rethrow? + catch (Exception e) { + LogUtils.DEBUG("** caught exception getting script data for " + name + + " ->" + e.toString()); + } + } + + // implementation of XInvocation interface + public XIntrospectionAccess getIntrospection() { + return null; + } + + public Object invoke(String aFunctionName, Object[] aParams, + short[][] aOutParamIndex, Object[][] aOutParam) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + + // Initialise the out parameters - not used but prevents error in + // UNO bridge + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + Any result = new Any(new Type(Boolean.class), Boolean.TRUE); + + if (aFunctionName.equals("Editable")) { + if (!editable) { + + NoSupportException nse = + new NoSupportException(aFunctionName + " is not editable "); + + throw new InvocationTargetException( + "Scripting framework error editing script", null, nse); + + } + + XScriptContext ctxt = provider.getScriptingContext(); + ScriptMetaData data = null; + + try { + data = parent.getByName(name); + } catch (NoSuchElementException nse) { + throw new com.sun.star.lang.IllegalArgumentException(nse, + name + " does not exist or can't be found "); + } catch (com.sun.star.lang.WrappedTargetException wte) { + // rethrow + throw new InvocationTargetException( + "Scripting framework editing script ", null, wte.TargetException); + } + + provider.getScriptEditor().edit(ctxt, data); + } else if (aFunctionName.equals("Deletable")) { + if (!deletable) { + + NoSupportException nse = new NoSupportException( + aFunctionName + " is not supported for this node"); + + throw new InvocationTargetException( + "Scripting framework error deleting script", null, nse); + } + + try { + parent.removeByName(name); + result = new Any(new Type(Boolean.class), Boolean.TRUE); + } catch (NoSuchElementException nse) { + throw new com.sun.star.lang.IllegalArgumentException(nse, + name + " does not exist or can't be found "); + } catch (WrappedTargetException wte) { + // rethrow + throw new InvocationTargetException( + "Scripting framework deleting script ", null, wte.TargetException); + } + + } else if (aFunctionName.equals("Renamable")) { + result = new Any(new Type(XBrowseNode.class), new XBrowseNode[0]); + + if (!renamable) { + + NoSupportException nse = new NoSupportException( + aFunctionName + " is not supported for this node"); + + throw new InvocationTargetException( + "Scripting framework error renaming script", null, nse); + } + + try { + String newName = AnyConverter.toString(aParams[0]); + ScriptMetaData oldData = parent.getByName(name); + oldData.loadSource(); + String oldSource = oldData.getSource(); + + LogUtils.DEBUG("Create renamed script"); + + String languageName = + newName + "." + provider.getScriptEditor().getExtension(); + + String language = provider.getName(); + + ScriptEntry entry = new ScriptEntry(language, languageName); + + ScriptMetaData data = + new ScriptMetaData(parent, entry, oldSource); + + parent.insertByName(languageName, data); + + LogUtils.DEBUG("Now remove old script"); + parent.removeByName(name); + + uri = data.getShortFormScriptURL(); + name = languageName; + result = new Any(new Type(XBrowseNode.class), this); + } catch (NoSuchElementException nse) { + throw new com.sun.star.lang.IllegalArgumentException(nse, + name + " does not exist or can't be found "); + } catch (ElementExistException eee) { + // rethrow + throw new InvocationTargetException( + "Scripting framework error renaming script ", null, eee); + } catch (WrappedTargetException wte) { + // rethrow + throw new InvocationTargetException( + "Scripting framework rename script ", null, wte.TargetException); + } + } else { + throw new com.sun.star.lang.IllegalArgumentException( + "Function " + aFunctionName + " not supported."); + } + + return result; + } + + public void setValue(String aPropertyName, Object aValue) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + } + + public Object getValue(String aPropertyName) throws + com.sun.star.beans.UnknownPropertyException { + + return null; + } + + public boolean hasMethod(String aName) { + return false; + } + + public boolean hasProperty(String aName) { + return false; + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/DeployedUnoPackagesDB.java b/scripting/java/com/sun/star/script/framework/container/DeployedUnoPackagesDB.java new file mode 100644 index 000000000..c187fb0e1 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/DeployedUnoPackagesDB.java @@ -0,0 +1,192 @@ +/* + * 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.script.framework.container; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.util.ArrayList; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +public class DeployedUnoPackagesDB { + + // This is the default contents of a parcel descriptor to be used when + // creating empty descriptors + private static final byte[] EMPTY_DOCUMENT = + ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + "<unopackages xmlns:unopackages=\"unopackages.dtd\">\n" + + "</unopackages>").getBytes(); + + private Document document = null; + + public DeployedUnoPackagesDB() throws IOException { + + ByteArrayInputStream bis = null; + + try { + bis = new ByteArrayInputStream(EMPTY_DOCUMENT); + this.document = XMLParserFactory.getParser().parse(bis); + } finally { + if (bis != null) + bis.close(); + } + } + + private DeployedUnoPackagesDB(Document document) { + this.document = document; + } + + public DeployedUnoPackagesDB(InputStream is) throws IOException { + this(XMLParserFactory.getParser().parse(is)); + } + + public String[] getDeployedPackages(String language) { + + ArrayList<String> packageUrls = new ArrayList<String>(4); + Element main = document.getDocumentElement(); + Element root = null; + int len = 0; + NodeList langNodes = null; + + if ((langNodes = main.getElementsByTagName("language")) != null && + (len = langNodes.getLength()) != 0) { + for (int i = 0; i < len; i++) { + Element e = (Element)langNodes.item(i); + + if (e.getAttribute("value").equals(language)) { + root = e; + break; + } + } + } + + if (root != null) { + len = 0; + NodeList packages = null; + + if ((packages = root.getElementsByTagName("package")) != null && + (len = packages.getLength()) != 0) { + + for (int i = 0; i < len; i++) { + Element e = (Element)packages.item(i); + packageUrls.add(e.getAttribute("value")); + } + + } + } + + if (!packageUrls.isEmpty()) { + return packageUrls.toArray(new String[packageUrls.size()]); + } + + return new String[0]; + } + + public void write(OutputStream out) throws IOException { + XMLParserFactory.getParser().write(document, out); + } + + public Document getDocument() { + return document; + } + + + public boolean removePackage(String language, String url) { + + Element main = document.getDocumentElement(); + Element langNode = null; + int len = 0; + NodeList langNodes = null; + + if ((langNodes = main.getElementsByTagName("language")) != null && + (len = langNodes.getLength()) != 0) { + for (int i = 0; i < len; i++) { + Element e = (Element)langNodes.item(i); + + if (e.getAttribute("value").equals(language)) { + langNode = e; + break; + } + } + } + + if (langNode == null) { + return false; + } + len = 0; + NodeList packages = null; + boolean result = false; + + if ((packages = langNode.getElementsByTagName("package")) != null && + (len = packages.getLength()) != 0) { + for (int i = 0; i < len; i++) { + + Element e = (Element)packages.item(i); + String value = e.getAttribute("value"); + + if (value.equals(url)) { + langNode.removeChild(e); + result = true; + break; + } + } + } + + return result; + } + + public void addPackage(String language, String url) { + + Element main = document.getDocumentElement(); + Element langNode = null; + Element pkgNode = null; + + int len = 0; + NodeList langNodes = null; + + if ((langNodes = document.getElementsByTagName("language")) != null && + (len = langNodes.getLength()) != 0) { + for (int i = 0; i < len; i++) { + Element e = (Element)langNodes.item(i); + + if (e.getAttribute("value").equals(language)) { + langNode = e; + break; + } + } + } + + if (langNode == null) { + langNode = document.createElement("language"); + langNode.setAttribute("value", language); + } + + pkgNode = document.createElement("package"); + pkgNode.setAttribute("value", url); + + langNode.appendChild(pkgNode); + //add to the Top Element + main.appendChild(langNode); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/container/Parcel.java b/scripting/java/com/sun/star/script/framework/container/Parcel.java new file mode 100644 index 000000000..6be4cad99 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/Parcel.java @@ -0,0 +1,302 @@ +/* + * 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.script.framework.container; + +import com.sun.star.container.ElementExistException; +import com.sun.star.container.XNameContainer; + +import com.sun.star.script.framework.io.XInputStreamImpl; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.ucb.XSimpleFileAccess; +import com.sun.star.ucb.XSimpleFileAccess2; + +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +public class Parcel implements XNameContainer { + + private ParcelDescriptor m_descriptor; + private String name; + protected ParcelContainer parent; + protected XSimpleFileAccess m_xSFA; + + public Parcel(XSimpleFileAccess xSFA, ParcelContainer parent, + ParcelDescriptor desc, String parcelName) { + + this(parent, desc, parcelName); + this.m_xSFA = xSFA; + } + + private Parcel(ParcelContainer parent, ParcelDescriptor desc, + String parcelName) { + + this.parent = parent; + this.m_descriptor = desc; + this.name = parcelName; + } + + /** + * Tests if this <tt>Parcel</tt> is in a UNO package + * or within a sub package within a UNO package + * + * @return <tt>true</tt> if has parent <tt>false</tt> otherwise + */ + public boolean isUnoPkg() { + return parent.isUnoPkg(); + } + + public String getName() { + return name; + } + + public ScriptMetaData getByName(String aName) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("** Parcel.getByName for " + aName); + ScriptEntry thescript = null; + + try { + if (m_descriptor != null && hasElements()) { + ScriptEntry[] scripts = m_descriptor.getScriptEntries(); + + if (scripts.length != 0) { + for (ScriptEntry script : scripts) { + if (script.getLanguageName().equals(aName)) { + thescript = script; + break; + } + } + } + } + } + // catch unknown or un-checked exceptions + catch (Exception e) { + throw new com.sun.star.lang.WrappedTargetException(e); + } + + if (thescript == null) { + LogUtils.DEBUG("No script for " + aName); + throw new com.sun.star.container.NoSuchElementException("No script named " + + aName); + } + + ScriptMetaData data = new ScriptMetaData(this, thescript, null); + + LogUtils.DEBUG("returning date for " + aName); + return data; + } + + public String[] getElementNames() { + + String[] results = new String[0]; + + if (m_descriptor != null) { + ScriptEntry[] scripts = m_descriptor.getScriptEntries(); + results = new String[ scripts.length ]; + + for (int index = 0; index < scripts.length; index++) { + results[ index ] = scripts[ index ].getLanguageName(); + } + } + + return results; + } + + public boolean hasByName(String aName) { + + boolean result = true; + ScriptMetaData containee = null; + + try { + containee = getByName(aName); + + if (containee != null) { + result = true; + } + } catch (Exception e) { + result = false; + } + + return result; + } + + public com.sun.star.uno.Type getElementType() { + // TODO at the moment this returns void indicating + // type is unknown ( from UNO point of view this is correct ) + // but, maybe we want to have a private UNO interface + + return new Type(); + } + + public boolean hasElements() { + return m_descriptor != null && m_descriptor.getScriptEntries().length > 0; + } + + public void replaceByName(String aName, java.lang.Object aElement) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + // TODO check type of aElement + // if not ok, throw IllegalArgument + if (m_descriptor != null) { + try { + ScriptMetaData script = getByName(aName); + + if (script != null) { + //m_descriptor.removeScriptEntry( script ); + // TODO needs to create source file ( if there is one ) + //m_descriptor.write(); + } else { + throw new com.sun.star.container.NoSuchElementException( + "No script named " + aName); + } + + } + // TO DO should catch specified exceptions + catch (Exception ex) { + throw new com.sun.star.lang.WrappedTargetException(ex); + } + + } + } + + // create + public void insertByName(String aName, java.lang.Object aElement) throws + com.sun.star.lang.IllegalArgumentException, ElementExistException, + com.sun.star.lang.WrappedTargetException { + + // TODO check the type of aElement and throw# + // if not appropriate + try { + if (hasByName(aName)) { + throw new ElementExistException(aName); + } + + ScriptMetaData script = (ScriptMetaData)aElement; + + if (script.hasSource()) { + LogUtils.DEBUG("Inserting source: " + script.getSource()); + + if (!script.writeSourceFile()) { + throw new com.sun.star.lang.WrappedTargetException( + "Failed to create source file " + script.getLanguageName()); + } + } + + m_descriptor.addScriptEntry(script); + writeParcelDescriptor(); + } catch (Exception e) { + LogUtils.DEBUG("Failed to insert entry " + aName + ": " + e.getMessage()); + throw new com.sun.star.lang.WrappedTargetException(e); + } + } + + private void writeParcelDescriptor() + throws com.sun.star.ucb.CommandAbortedException, + com.sun.star.io.IOException, + com.sun.star.uno.Exception, java.io.IOException { + + String pathToDescriptor = + PathUtils.make_url(getPathToParcel(), ParcelDescriptor.PARCEL_DESCRIPTOR_NAME); + + XSimpleFileAccess2 xSFA2 = + UnoRuntime.queryInterface(XSimpleFileAccess2.class, m_xSFA); + + if (xSFA2 != null) { + + ByteArrayOutputStream bos = null; + ByteArrayInputStream bis = null; + XInputStreamImpl xis = null; + + try { + bos = new ByteArrayOutputStream(1024); + m_descriptor.write(bos); + bis = new ByteArrayInputStream(bos.toByteArray()); + + xis = new XInputStreamImpl(bis); + xSFA2.writeFile(pathToDescriptor, xis); + } finally { + if (bos != null) bos.close(); + + if (bis != null) bis.close(); + + if (xis != null) xis.closeInput(); + } + } + } + + // delete + public void removeByName(String Name) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + try { + ScriptMetaData script = getByName(Name); + + if (script != null) { + if (!script.removeSourceFile()) { + LogUtils.DEBUG("** Parcel.removeByName Failed to remove script " + + Name); + throw new com.sun.star.lang.WrappedTargetException( + "Failed to remove script " + Name); + } + + LogUtils.DEBUG("** Parcel.removeByName have removed script source file " + + Name); + + m_descriptor.removeScriptEntry(script); + writeParcelDescriptor(); + } else { + throw new com.sun.star.container.NoSuchElementException( + "No script named " + Name); + } + + } catch (Exception e) { + LogUtils.DEBUG("** Parcel.removeByName Exception: " + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + } + + // rename parcel + public void rename(String name) { + this.name = name; + } + + public ParcelContainer getParent() { + return parent; + } + + /** + * Returns the path of this <tt>Parcel</tt> + * + * @return <tt>String</tt> path to parcel + */ + public String getPathToParcel() { + String path = parent.getParcelContainerDir() + "/" + name; + return path; + } + +} diff --git a/scripting/java/com/sun/star/script/framework/container/ParcelContainer.java b/scripting/java/com/sun/star/script/framework/container/ParcelContainer.java new file mode 100644 index 000000000..1fca1419f --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/ParcelContainer.java @@ -0,0 +1,725 @@ +/* + * 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.script.framework.container; + +import com.sun.star.container.ElementExistException; +import com.sun.star.container.XNameAccess; +import com.sun.star.container.XNameContainer; + +import com.sun.star.io.XInputStream; + +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XMultiComponentFactory; + +import com.sun.star.script.framework.io.XInputStreamImpl; +import com.sun.star.script.framework.io.XInputStreamWrapper; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.ucb.XSimpleFileAccess; +import com.sun.star.ucb.XSimpleFileAccess2; + +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import com.sun.star.uri.XUriReference; +import com.sun.star.uri.XUriReferenceFactory; +import com.sun.star.uri.XVndSunStarScriptUrl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.StringTokenizer; + +/** + * The <code>ParcelContainer</code> object is used to store the + * ScripingFramework specific Libraries. + */ +public class ParcelContainer implements XNameAccess { + + protected static XSimpleFileAccess m_xSFA; + + protected String language; + protected String containerUrl; + private Collection<Parcel> parcels = new ArrayList<Parcel>(10); + protected XComponentContext m_xCtx; + private ParcelContainer parent = null; + private final Collection<ParcelContainer> childContainers = new ArrayList<ParcelContainer>(10); + private boolean isPkgContainer = false; + + /** + * Tests if this <tt>ParcelContainer</tt> represents a UNO package + * or sub package within a UNO package + * + * @return <tt>true</tt> if has parent <tt>false</tt> otherwise + */ + public boolean isUnoPkg() { + return isPkgContainer; + } + + /** + * Returns this <tt>ParcelContainer</tt>'s parent + * + * @return <tt>ParcelContainer</tt> if has parent null otherwise + */ + public ParcelContainer parent() { + return parent; + } + + /** + * Returns all child <tt>ParcelContainer</tt> + * this instance of <tt>ParcelContainer</tt> + * + * @return a new array of ParcelContainers. A zero + * length array is returned if no child ParcelContainers. + */ + public ParcelContainer[] getChildContainers() { + if (childContainers.isEmpty()) { + return new ParcelContainer[0]; + } + + return childContainers.toArray(new ParcelContainer[childContainers.size()]); + } + + /** + * Removes a child <tt>ParcelContainer</tt> + * from this instance. + * @param child <tt>ParcelContainer</tt> to be added. + * + * @return <tt>true</tt> if child successfully removed + */ + public boolean removeChildContainer(ParcelContainer child) { + return childContainers.remove(child); + } + + /** + * Adds a new child <tt>ParcelContainer</tt> + * to this instance. + * @param child <tt>ParcelContainer</tt> to be added. + * + */ + public void addChildContainer(ParcelContainer child) { + childContainers.add(child); + } + + /** + * Returns a child <tt>ParcelContainer</tt> whose location + * matches the <tt>location</tt> argument passed to this method. + * @param key the <tt>location</tt> which is to + * be matched. + * + * @return child <tt>ParcelContainer</tt> or {@code null} if none + * found. + */ + public ParcelContainer getChildContainer(String key) { + ParcelContainer result = null; + + for (ParcelContainer c : childContainers) { + + String name = c.getName(); + if (name == null) + { + continue; + } + + String location = + ScriptMetaData.getLocationPlaceHolder(c.containerUrl, name); + + if (key.equals(location)) { + result = c; + break; + } + } + + return result; + } + + /** + * Returns a child <tt>ParcelContainer</tt> whose member + * <tt>containerUrl</tt> matches the <tt>containerUrl</tt> + * argument passed to this method. + * @param containerUrl the <tt>containerUrl</tt> which is to + * be matched. + * + * @return child <tt>ParcelContainer</tt> or {@code null} if none + * found. + */ + public ParcelContainer getChildContainerForURL(String containerUrl) { + ParcelContainer result = null; + + for (ParcelContainer c : childContainers) { + if (containerUrl.equals(c.containerUrl)) { + result = c; + break; + } + } + + return result; + } + + /** + * Returns Name of this container. Name of this <tt>ParcelContainer</tt> + * is determined from the <tt>containerUrl</tt> as the last portion + * of the URL after the last forward slash. + * @return name of <tt>ParcelContainer</tt> + * found. + */ + public String getName() { + String name = null; + + // TODO handler package ParcelContainer? + if (!containerUrl.startsWith("vnd.sun.star.tdoc:")) { + try { + // return name + String decodedUrl = java.net.URLDecoder.decode(containerUrl, "UTF-8"); + int indexOfSlash = decodedUrl.lastIndexOf('/'); + + if (indexOfSlash != -1) { + name = decodedUrl.substring(indexOfSlash + 1); + } + } catch (UnsupportedEncodingException e) { + throw new com.sun.star.uno.RuntimeException(e); + } + } else { + name = "document"; + } + + return name; + } + + /** + * Initializes a newly created <code>ParcelContainer</code> object. + * @param xCtx UNO component context + * @param containerUrl location of this container. + * @param language language for which entries are stored + */ + public ParcelContainer(XComponentContext xCtx, String containerUrl, + String language) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + this(null, xCtx, containerUrl, language, true); + } + + /** + * Initializes a newly created <code>ParcelContainer</code> object. + * @param xCtx UNO component context + * @param containerUrl location of this container. + * @param language language for which entries are stored + * @param loadParcels set to <tt>true</tt> if parcels are to be loaded + * on construction. + */ + public ParcelContainer(XComponentContext xCtx, String containerUrl, + String language, boolean loadParcels) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + this(null, xCtx, containerUrl, language, loadParcels); + } + + /** + * Initializes a newly created <code>ParcelContainer</code> object. + * @param parent parent ParcelContainer + * @param xCtx UNO component context + * @param containerUrl location of this container. + * @param language language for which entries are stored + * @param loadParcels set to <tt>true</tt> if parcels are to be loaded + * on construction. + */ + public ParcelContainer(ParcelContainer parent, XComponentContext xCtx, + String containerUrl, String language, + boolean loadParcels) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("Creating ParcelContainer for " + containerUrl + + " loadParcels = " + loadParcels + " language = " + language); + + this.m_xCtx = xCtx; + this.language = language; + this.parent = parent; + this.containerUrl = containerUrl; + + initSimpleFileAccess(); + boolean parentIsPkgContainer = false; + + if (parent != null) { + parentIsPkgContainer = parent.isUnoPkg(); + } + + if (containerUrl.endsWith("uno_packages") || parentIsPkgContainer) { + isPkgContainer = true; + } + + if (loadParcels) { + loadParcels(); + } + } + + + public String getContainerURL() { + return this.containerUrl; + } + + private void initSimpleFileAccess() { + synchronized (ParcelContainer.class) { + if (m_xSFA != null) { + return; + } + + try { + + m_xSFA = UnoRuntime.queryInterface( + XSimpleFileAccess.class, + m_xCtx.getServiceManager().createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", m_xCtx)); + + } catch (Exception e) { + // TODO should throw + LogUtils.DEBUG("Error instantiating simplefile access "); + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + } + } + + public String getParcelContainerDir() { + // If this container does not represent a uno-package + // then it is a document, user or share + // in each case the convention is to have a Scripts/[language] + // dir where scripts reside + if (!isUnoPkg()) { + return PathUtils.make_url(containerUrl , "Scripts/" + language.toLowerCase()); + } + + return containerUrl; + } + + public Object getByName(String aName) throws + com.sun.star.container.NoSuchElementException, WrappedTargetException { + + Parcel parcel = null; + + try { + if (hasElements()) { + for (Parcel parcelToCheck : parcels) { + if (parcelToCheck.getName().equals(aName)) { + parcel = parcelToCheck; + break; + } + } + } + } catch (Exception e) { + throw new WrappedTargetException(e); + } + + if (parcel == null) { + throw new com.sun.star.container.NoSuchElementException("Macro Library " + aName + + " not found"); + } + + return parcel; + } + + public String[] getElementNames() { + + if (hasElements()) { + + Parcel[] theParcels = parcels.toArray(new Parcel[parcels.size()]); + String[] names = new String[ theParcels.length ]; + + for (int count = 0; count < names.length; count++) { + names[count] = theParcels[ count ].getName(); + } + + return names; + } + + return new String[0]; + } + + public boolean hasByName(String aName) { + + boolean isFound = false; + + try { + if (getByName(aName) != null) { + isFound = true; + } + + } catch (Exception e) { + //TODO - handle trace + } + + return isFound; + } + + public Type getElementType() { + return new Type(); + } + + public boolean hasElements() { + return !(parcels == null || parcels.isEmpty()); + } + + private void loadParcels() throws com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + try { + LogUtils.DEBUG("About to load parcels from " + containerUrl); + + if (m_xSFA.isFolder(getParcelContainerDir())) { + LogUtils.DEBUG(getParcelContainerDir() + " is a folder "); + String[] children = m_xSFA.getFolderContents(getParcelContainerDir(), true); + parcels = new ArrayList<Parcel>(children.length); + + for (String child : children) { + LogUtils.DEBUG("Processing " + child); + + try { + loadParcel(child); + } catch (java.lang.Exception e) { + // print an error message and move on to + // the next parcel + LogUtils.DEBUG("ParcelContainer.loadParcels caught " + e.getClass().getName() + + " exception loading parcel " + child + ": " + e.getMessage()); + } + } + } else { + LogUtils.DEBUG(" ParcelCOntainer.loadParcels " + getParcelContainerDir() + + " is not a folder "); + } + + } catch (com.sun.star.ucb.CommandAbortedException e) { + LogUtils.DEBUG("ParcelContainer.loadParcels caught exception processing folders for " + + getParcelContainerDir()); + LogUtils.DEBUG("TRACE: " + LogUtils.getTrace(e)); + throw new com.sun.star.lang.WrappedTargetException(e); + } catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG("ParcelContainer.loadParcels caught exception processing folders for " + + getParcelContainerDir()); + LogUtils.DEBUG("TRACE: " + LogUtils.getTrace(e)); + throw new com.sun.star.lang.WrappedTargetException(e); + } + } + + public XNameContainer createParcel(String name) throws + ElementExistException, com.sun.star.lang.WrappedTargetException { + + Parcel p = null; + + if (hasByName(name)) { + throw new ElementExistException("Parcel " + name + " already exists"); + } + + String pathToParcel = PathUtils.make_url(getParcelContainerDir(), name); + + try { + LogUtils.DEBUG("ParcelContainer.createParcel, creating folder " + + pathToParcel); + + m_xSFA.createFolder(pathToParcel); + + LogUtils.DEBUG("ParcelContainer.createParcel, folder " + pathToParcel + + " created "); + + ParcelDescriptor pd = new ParcelDescriptor(); + pd.setLanguage(language); + + String parcelDesc = + PathUtils.make_url(pathToParcel, ParcelDescriptor.PARCEL_DESCRIPTOR_NAME); + + XSimpleFileAccess2 xSFA2 = + UnoRuntime.queryInterface(XSimpleFileAccess2.class, m_xSFA); + + if (xSFA2 != null) { + LogUtils.DEBUG("createParcel() Using XSIMPLEFILEACCESS2 " + parcelDesc); + ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); + pd.write(bos); + bos.close(); + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + XInputStreamImpl xis = new XInputStreamImpl(bis); + xSFA2.writeFile(parcelDesc, xis); + xis.closeInput(); + p = loadParcel(pathToParcel); + } + } catch (Exception e) { + LogUtils.DEBUG("createParcel() Exception while attempting to create = " + + name); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + return p; + } + + public Parcel loadParcel(String parcelUrl) throws + com.sun.star.lang.WrappedTargetException, + com.sun.star.lang.IllegalArgumentException { + + String parcelDescUrl = + PathUtils.make_url(parcelUrl, ParcelDescriptor.PARCEL_DESCRIPTOR_NAME); + + Parcel parcel = null; + XInputStream xis = null; + InputStream is = null; + + try { + if (m_xSFA.exists(parcelDescUrl)) { + + LogUtils.DEBUG("ParcelContainer.loadParcel opening " + parcelDescUrl); + + xis = m_xSFA.openFileRead(parcelDescUrl); + is = new XInputStreamWrapper(xis); + ParcelDescriptor pd = new ParcelDescriptor(is) ; + + try { + is.close(); + is = null; + } catch (Exception e) { + LogUtils.DEBUG( + "ParcelContainer.loadParcel Exception when closing stream for " + + parcelDescUrl + " :" + e); + } + + LogUtils.DEBUG("ParcelContainer.loadParcel closed " + parcelDescUrl); + + if (!pd.getLanguage().equals(language)) { + LogUtils.DEBUG("ParcelContainer.loadParcel Language of Parcel does not match this container "); + return null; + } + + LogUtils.DEBUG("Processing " + parcelDescUrl + " closed "); + + int indexOfSlash = parcelUrl.lastIndexOf('/'); + String name = parcelUrl.substring(indexOfSlash + 1); + + parcel = new Parcel(m_xSFA, this, pd, name); + + LogUtils.DEBUG(" ParcelContainer.loadParcel created parcel for " + + parcelDescUrl + " for language " + language); + + parcels.add(parcel); + } else { + throw new java.io.IOException(parcelDescUrl + " does NOT exist!"); + } + } catch (com.sun.star.ucb.CommandAbortedException e) { + LogUtils.DEBUG("loadParcel() Exception while accessing filesystem url = " + + parcelDescUrl + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } catch (java.io.IOException e) { + LogUtils.DEBUG("ParcelContainer.loadParcel() caught IOException while accessing " + + parcelDescUrl + ": " + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG("loadParcel() Exception while accessing filesystem url = " + + parcelDescUrl + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + finally { + if (is != null) { + try { + is.close(); // is will close xis + } catch (Exception ignore) { + } + } else if (xis != null) { + try { + xis.closeInput(); + } catch (Exception ignore) { + } + } + } + + return parcel; + } + public void renameParcel(String oldName, String newName) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG(" ** ParcelContainer Renaming parcel " + oldName + " to " + + newName); + LogUtils.DEBUG(" ** ParcelContainer is " + this); + + Parcel p = (Parcel)getByName(oldName); + + if (p == null) { + throw new com.sun.star.container.NoSuchElementException( + "No parcel named " + oldName); + } + + String oldParcelDirUrl = + PathUtils.make_url(getParcelContainerDir(), oldName); + + String newParcelDirUrl = + PathUtils.make_url(getParcelContainerDir(), newName); + + try { + if (!m_xSFA.isFolder(oldParcelDirUrl)) { + Exception e = new com.sun.star.io.IOException( + "Invalid Parcel directory: " + oldName); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + LogUtils.DEBUG(" ** ParcelContainer Renaming folder " + oldParcelDirUrl + + " to " + newParcelDirUrl); + + m_xSFA.move(oldParcelDirUrl, newParcelDirUrl); + + } catch (com.sun.star.ucb.CommandAbortedException ce) { + LogUtils.DEBUG(" ** ParcelContainer Renaming failed with " + ce); + throw new com.sun.star.lang.WrappedTargetException(ce); + } catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG(" ** ParcelContainer Renaming failed with " + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + p.rename(newName); + } + // removes but doesn't physically delete parcel from container + public boolean removeParcel(String name) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + Parcel p = (Parcel)getByName(name); + + if (p == null) { + throw new com.sun.star.container.NoSuchElementException( + "No parcel named " + name); + } + + return parcels.remove(p); + } + + public boolean deleteParcel(String name) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("deleteParcel for containerURL " + containerUrl + + " name = " + name + " Language = " + language); + + Parcel p = (Parcel)getByName(name); + + if (p == null) { + throw new com.sun.star.container.NoSuchElementException( + "No parcel named " + name); + } + + try { + String pathToParcel = PathUtils.make_url(getParcelContainerDir(), name); + m_xSFA.kill(pathToParcel); + } catch (Exception e) { + LogUtils.DEBUG("Error deleting parcel " + name); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + return parcels.remove(p); + } + + public String getLanguage() { + return language; + } + + public ScriptMetaData findScript(ParsedScriptUri parsedUri) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + Parcel p = (Parcel)getByName(parsedUri.parcel); + ScriptMetaData scriptData = p.getByName(parsedUri.function); + + LogUtils.DEBUG("** found script data for " + parsedUri.function + " script is " + + scriptData); + + return scriptData; + } + + public ParsedScriptUri parseScriptUri(String scriptURI) throws + com.sun.star.lang.IllegalArgumentException { + + XMultiComponentFactory xMcFac = null; + XUriReferenceFactory xFac = null; + + try { + xMcFac = m_xCtx.getServiceManager(); + + xFac = UnoRuntime.queryInterface( + XUriReferenceFactory.class, xMcFac.createInstanceWithContext( + "com.sun.star.uri.UriReferenceFactory", m_xCtx)); + + } catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG("Problems parsing URL:" + e.toString()); + throw new com.sun.star.lang.IllegalArgumentException( + e, "Problems parsing URL"); + } + + if (xFac == null) { + LogUtils.DEBUG("Failed to create UrlReference factory"); + throw new com.sun.star.lang.IllegalArgumentException( + "Failed to create UrlReference factory for url " + scriptURI); + } + + XUriReference uriRef = xFac.parse(scriptURI); + + XVndSunStarScriptUrl sfUri = + UnoRuntime.queryInterface(XVndSunStarScriptUrl.class, uriRef); + + if (sfUri == null) { + LogUtils.DEBUG("Failed to parse url"); + throw new com.sun.star.lang.IllegalArgumentException( + "Failed to parse url " + scriptURI); + } + + ParsedScriptUri parsedUri = new ParsedScriptUri(); + parsedUri.function = sfUri.getName(); + parsedUri.parcel = ""; + + // parse parcel name; + StringTokenizer tokenizer = new StringTokenizer(parsedUri.function, "."); + + if (tokenizer.hasMoreElements()) { + parsedUri.parcel = (String)tokenizer.nextElement(); + LogUtils.DEBUG("** parcelName = " + parsedUri.parcel); + } + + if (parsedUri.function.length() > 0) { + + // strip out parcel name + parsedUri.function = + parsedUri.function.substring(parsedUri.parcel.length() + 1); + + } + + // parse location + parsedUri.location = sfUri.getParameter("location"); + + // TODO basic sanity check on language, location, function name, parcel + // should be correct e.g. verified by MSP and LangProvider by the + // time it's got to here + + LogUtils.DEBUG("** location = " + parsedUri.location + + "\nfunction = " + parsedUri.function + + "\nparcel = " + parsedUri.parcel + + "\nlocation = " + parsedUri.location); + + return parsedUri; + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/ParcelDescriptor.java b/scripting/java/com/sun/star/script/framework/container/ParcelDescriptor.java new file mode 100644 index 000000000..eeb8f9ef2 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/ParcelDescriptor.java @@ -0,0 +1,354 @@ +/* + * 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.script.framework.container; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +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.Map; + +import org.w3c.dom.CharacterData; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +public class ParcelDescriptor { + + // File name to be used for parcel descriptor files + public static final String + PARCEL_DESCRIPTOR_NAME = "parcel-descriptor.xml"; + + // This is the default contents of a parcel descriptor to be used when + // creating empty descriptors + private static final byte[] EMPTY_DOCUMENT = + ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + "<parcel xmlns:parcel=\"scripting.dtd\" language=\"Java\">\n" + + "</parcel>").getBytes(); + + private Document document = null; + private String language = null; + private final Map<String, String> languagedepprops = new HashMap<String, String>(3); + + public ParcelDescriptor() throws IOException { + ByteArrayInputStream bis = null; + + try { + bis = new ByteArrayInputStream(EMPTY_DOCUMENT); + this.document = XMLParserFactory.getParser().parse(bis); + } finally { + if (bis != null) + bis.close(); + } + } + + private ParcelDescriptor(Document document) { + this.document = document; + initLanguageProperties(); + } + + public ParcelDescriptor(InputStream is) throws IOException { + this(XMLParserFactory.getParser().parse(is)); + } + + public ParcelDescriptor(File file) throws IOException { + this(file, "Java"); + } + + private ParcelDescriptor(File file, String language) throws IOException { + if (file.exists()) { + FileInputStream fis = null; + + try { + fis = new FileInputStream(file); + this.document = XMLParserFactory.getParser().parse(fis); + } finally { + if (fis != null) + fis.close(); + } + } else { + ByteArrayInputStream bis = null; + + try { + bis = new ByteArrayInputStream(EMPTY_DOCUMENT); + this.document = XMLParserFactory.getParser().parse(bis); + } finally { + if (bis != null) + bis.close(); + } + + setLanguage(language); + } + + initLanguageProperties(); + } + + public void write(OutputStream out) throws IOException { + XMLParserFactory.getParser().write(document, out); + } + + public Document getDocument() { + return document; + } + + public String getLanguage() { + if (language == null && document != null) { + Element e = document.getDocumentElement(); + language = e.getAttribute("language"); + } + + return language; + } + + public void setLanguage(String language) { + this.language = language; + + if (document != null) { + try { + Element e = document.getDocumentElement(); + e.setAttribute("language", language); + } catch (DOMException de) { + } + } + } + + public ScriptEntry[] getScriptEntries() { + + ArrayList<ScriptEntry> scripts = new ArrayList<ScriptEntry>(); + NodeList scriptNodes; + int len; + + if (document == null || + (scriptNodes = document.getElementsByTagName("script")) == null || + (len = scriptNodes.getLength()) == 0) + return new ScriptEntry[0]; + + for (int i = 0; i < len; i++) { + String language, languagename, description = ""; + Map<String, String> langProps = new HashMap<String, String>(); + NodeList nl; + + Element scriptElement = (Element)scriptNodes.item(i); + language = scriptElement.getAttribute("language"); + + // get the text of the description element + nl = scriptElement.getElementsByTagName("locale"); + + if (nl != null) { + nl = nl.item(0).getChildNodes(); + + if (nl != null) { + for (int j = 0 ; j < nl.getLength(); j++) { + if (nl.item(j).getNodeName().equals("description")) { + CharacterData cd = + (CharacterData)nl.item(j).getFirstChild(); + description = cd.getData().trim(); + } + } + } + } + + nl = scriptElement.getElementsByTagName("functionname"); + + if (nl == null) { + languagename = ""; + } else { + Element tmp = (Element)nl.item(0); + languagename = tmp.getAttribute("value"); + } + + nl = scriptElement.getElementsByTagName("languagedepprops"); + + if (nl != null && nl.getLength() > 0) { + + NodeList props = ((Element)nl.item(0)).getElementsByTagName("prop"); + + if (props != null) { + for (int j = 0; j < props.getLength(); j++) { + Element tmp = (Element)props.item(j); + String key = tmp.getAttribute("name"); + String val = tmp.getAttribute("value"); + langProps.put(key, val); + } + } + } + + ScriptEntry entry = + new ScriptEntry(language, languagename, langProps, description); + scripts.add(entry); + } + + return scripts.toArray(new ScriptEntry[scripts.size()]); + } + + public void setScriptEntries(ScriptEntry[] scripts) { + clearEntries(); + + for (ScriptEntry script : scripts) { + addScriptEntry(script); + } + } + + public void setScriptEntries(Iterator<ScriptEntry> scripts) { + clearEntries(); + + while (scripts.hasNext()) { + addScriptEntry(scripts.next()); + } + } + + private String getLanguageProperty(String name) { + return languagedepprops.get(name); + } + + + + private void initLanguageProperties() { + NodeList nl = document.getElementsByTagName("languagedepprops"); + int len; + + if (nl != null && (len = nl.getLength()) != 0) { + + for (int i = 0; i < len; i++) { + Element e = (Element)nl.item(i); + NodeList nl2 = e.getElementsByTagName("prop"); + int len2; + + if (nl2 != null && (len2 = nl2.getLength()) != 0) { + for (int j = 0; j < len2; j++) { + Element e2 = (Element)nl2.item(j); + + String name = e2.getAttribute("name"); + String value = e2.getAttribute("value"); + + if (getLanguageProperty(name) == null) { + languagedepprops.put(name, value); + } + } + } + } + } + } + + private void clearEntries() { + NodeList scriptNodes; + Element main = document.getDocumentElement(); + int len; + + if ((scriptNodes = document.getElementsByTagName("script")) != null && + (len = scriptNodes.getLength()) != 0) { + for (int i = len - 1; i >= 0; i--) { + try { + main.removeChild(scriptNodes.item(i)); + } catch (DOMException de) { + // ignore + } + } + } + } + + public void removeScriptEntry(ScriptEntry script) { + NodeList scriptNodes; + Element main = document.getDocumentElement(); + int len; + + if ((scriptNodes = document.getElementsByTagName("script")) != null && + (len = scriptNodes.getLength()) != 0) { + for (int i = len - 1; i >= 0; i--) { + try { + Element scriptElement = (Element)scriptNodes.item(i); + String languagename = ""; + + NodeList nl = + scriptElement.getElementsByTagName("functionname"); + + if (nl == null) { + continue; + } else { + Element tmp = (Element)nl.item(0); + languagename = tmp.getAttribute("value"); + } + + if (languagename.equals(script.getLanguageName())) { + main.removeChild(scriptElement); + } + } catch (DOMException de) { + // ignore + } + } + } + } + + public void addScriptEntry(ScriptEntry script) { + Element main = document.getDocumentElement(); + Element root, item, tempitem; + + root = document.createElement("script"); + root.setAttribute("language", script.getLanguage()); + + item = document.createElement("locale"); + item.setAttribute("lang", "en"); + tempitem = document.createElement("displayname"); + tempitem.setAttribute("value", script.getLogicalName()); + item.appendChild(tempitem); + + tempitem = document.createElement("description"); + String description = script.getDescription(); + + if (description == null || description.length() == 0) { + description = script.getLogicalName(); + } + + tempitem.appendChild(document.createTextNode(description)); + item.appendChild(tempitem); + + root.appendChild(item); + + item = document.createElement("logicalname"); + item.setAttribute("value", script.getLogicalName()); + root.appendChild(item); + + item = document.createElement("functionname"); + item.setAttribute("value", script.getLanguageName()); + root.appendChild(item); + + if (languagedepprops != null && !languagedepprops.isEmpty()) { + String key; + item = document.createElement("languagedepprops"); + + for (Map.Entry<String, String> entry : languagedepprops.entrySet()) { + tempitem = document.createElement("prop"); + tempitem.setAttribute("name", entry.getKey()); + tempitem.setAttribute("value", entry.getValue()); + item.appendChild(tempitem); + } + + root.appendChild(item); + } + + //add to the Top Element + main.appendChild(root); + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/ParsedScriptUri.java b/scripting/java/com/sun/star/script/framework/container/ParsedScriptUri.java new file mode 100644 index 000000000..796314f00 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/ParsedScriptUri.java @@ -0,0 +1,25 @@ +/* + * 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.script.framework.container; + +public class ParsedScriptUri { + + public String location; + public String function; + public String parcel; +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/container/ScriptEntry.java b/scripting/java/com/sun/star/script/framework/container/ScriptEntry.java new file mode 100644 index 000000000..92870c680 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/ScriptEntry.java @@ -0,0 +1,85 @@ +/* + * 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.script.framework.container; + +import java.util.HashMap; +import java.util.Map; + +public class ScriptEntry { + + private final String language; + private final String languagename; + private final String logicalname; + private final String description; + + private final Map<String, String> languagedepprops; + + protected ScriptEntry(ScriptEntry entry) { + this.language = entry.language; + this.languagename = entry.languagename; + this.logicalname = entry.languagename; + this.languagedepprops = entry.languagedepprops; + this.description = entry.description; + } + + public ScriptEntry(String language, String languagename) { + this(language, languagename, new HashMap<String, String>(), ""); + } + + public ScriptEntry(String language, String languagename, + Map<String, String> languagedepprops, + String description) { + this.language = language; + this.languagename = languagename; + // logical name/ function name concept + // needs to be reworked, in meantime + // function name ( from xml ) will be used + // as logical name also + this.logicalname = languagename; + this.languagedepprops = languagedepprops; + this.description = description; + } + + public Map<String, String> getLanguageProperties() { + return languagedepprops; + } + + public String getLanguageName() { + return languagename; + } + + public String getLogicalName() { + return logicalname; + } + + public String getLanguage() { + return language; + } + + public String getDescription() { + return description; + } + + @Override + public String toString() { + return "\nLogicalName = " + logicalname + + "\nLanguageName = " + languagename + + "\nLanguaguageProperties = " + languagedepprops; + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/ScriptMetaData.java b/scripting/java/com/sun/star/script/framework/container/ScriptMetaData.java new file mode 100644 index 000000000..de51b1247 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/ScriptMetaData.java @@ -0,0 +1,328 @@ +/* + * 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.script.framework.container; + +import com.sun.star.script.framework.io.UCBStreamHandler; +import com.sun.star.script.framework.io.XInputStreamImpl; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.ucb.XSimpleFileAccess2; + +import com.sun.star.uno.UnoRuntime; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import java.net.URL; + +import java.util.ArrayList; +import java.util.StringTokenizer; + +public class ScriptMetaData extends ScriptEntry { + + private boolean hasSource = false; + private static final String locationPlaceHolder = ""; + private String source; + private final Parcel parent; + + + public ScriptMetaData(Parcel parent, ScriptEntry entry, + String source) { + super(entry); + this.parent = parent; + + if (source != null) { + this.hasSource = true; + this.source = source; + } + } + + public boolean hasSource() { + return hasSource; + } + + public String getSource() { + return (source != null && hasSource) ? source : null; + } + + public byte[] getSourceBytes() { + return (source != null && hasSource) ? source.getBytes() : null; + } + + public String getScriptFullURL() { + String url = "vnd.sun.star.script:" + parent.getName() + "." + + getLanguageName() + "?" + "language=" + getLanguage() + + "&location=" + getParcelLocation(); + + return url; + } + + public String getShortFormScriptURL() { + String url = "vnd.sun.star.script:" + parent.getName() + "." + + getLanguageName() + "?" + "language=" + getLanguage() + + "&location=" + getLocationPlaceHolder(); + + return url; + } + + // TODO probably should be private should not be necessary + // to be exposed at all + + private static final String SHARE = + "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR"; + + private static final String USER = + "vnd.sun.star.expand:${$BRAND_INI_DIR/" + PathUtils.BOOTSTRAP_NAME + + "::UserInstallation}/user"; + + private static final String UNO_USER_PACKAGES1 = + "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE"; + + private static final String UNO_USER_PACKAGES2 = USER + "/uno_packages"; + + private static final String UNO_SHARED_PACKAGES1 = + "$UNO_SHARED_PACKAGES_CACHE"; + + private static final String UNO_SHARED_PACKAGES2 = SHARE + "/uno_packages"; + + public static String getFileName(URL url) { + String fileName = url.toExternalForm(); + if (fileName.lastIndexOf(UCBStreamHandler.separator) != -1) { + fileName = fileName.substring(0, fileName.lastIndexOf(UCBStreamHandler.separator)); + fileName = fileName.substring(fileName.lastIndexOf("/") + 1); + } + return fileName; + } + + public static String getLocationPlaceHolder(String url, String pkgname) { + String result = "Unknown"; + + if (url.contains(UNO_USER_PACKAGES1) || + url.contains(UNO_USER_PACKAGES2)) { + result = PathUtils.make_url("user:uno_packages", pkgname); + } else if (url.contains(UNO_SHARED_PACKAGES1) || + url.contains(UNO_SHARED_PACKAGES2)) { + result = PathUtils.make_url("share:uno_packages", pkgname); + } else if (url.indexOf(SHARE) == 0) { + result = "share"; + } else if (url.indexOf(USER) == 0) { + result = "user"; + } else if (url.indexOf("vnd.sun.star.tdoc:") == 0) { + result = "document"; + } + + return result; + } + + public String getLocationPlaceHolder() { + String placeHolder = "Unknown"; + String pathToParcel = parent.getPathToParcel(); + + if (pathToParcel.contains(UNO_USER_PACKAGES1) || + pathToParcel.contains(UNO_USER_PACKAGES2)) { + // it's a package + placeHolder = "user:uno_packages"; + String unoPkg = parent.parent.getName(); + + if (unoPkg != null) { + placeHolder = PathUtils.make_url(placeHolder, unoPkg); + } + } else if (pathToParcel.contains(UNO_SHARED_PACKAGES1) || + pathToParcel.contains(UNO_SHARED_PACKAGES2)) { + //it's a package + placeHolder = "share:uno_packages"; + String unoPkg = parent.parent.getName(); + + if (unoPkg != null) { + placeHolder = PathUtils.make_url(placeHolder, unoPkg); + } + } else if (pathToParcel.indexOf(SHARE) == 0) { + placeHolder = "share"; + } else if (pathToParcel.indexOf(USER) == 0) { + placeHolder = "user"; + } else if (pathToParcel.indexOf("vnd.sun.star.tdoc:") == 0) { + placeHolder = "document"; + } + + // TODO handling document packages ??? not really sure of package url + /* else + { + } */ + return placeHolder; + } + + // TODO probably should be private should not be necessary + // to be exposed at all only used in lang providers at the moment + // to generate URL for script, editors should use a model of script + // source and not interact with the URL + // Also if it is to remain needs to be renamed to getParcelLocationURL + + // return URL string to parcel + public String getParcelLocation() { + return parent.getPathToParcel(); + } + + @Override + public String toString() { + return "\nParcelLocation = " + getParcelLocation() + "\nLocationPlaceHolder = " + + locationPlaceHolder + super.toString(); + } + + public URL[] getClassPath() { + try { + String classpath = getLanguageProperties().get("classpath"); + + if (classpath == null) { + classpath = ""; + } + + String parcelPath = getParcelLocation(); + + // make sure path ends with / + if (!parcelPath.endsWith("/")) { + parcelPath += "/"; + } + + // replace \ with / + parcelPath = parcelPath.replace('\\', '/'); + + ArrayList<URL> classPathVec = new ArrayList<URL>(); + StringTokenizer stk = new StringTokenizer(classpath, ":"); + + while (stk.hasMoreElements()) { + String relativeClasspath = stk.nextToken(); + String pathToProcess = PathUtils.make_url(parcelPath, relativeClasspath); + URL url = createURL(pathToProcess); + + if (url != null) { + classPathVec.add(url); + } + + } + + if (classPathVec.isEmpty()) { + URL url = createURL(parcelPath); + + if (url != null) { + classPathVec.add(url); + } + } + + return classPathVec.toArray(new URL[classPathVec.size()]); + } catch (Exception e) { + LogUtils.DEBUG("Failed to build class path " + e.toString()); + LogUtils.DEBUG(LogUtils.getTrace(e)); + return new URL[0]; + } + } + + private URL createURL(String path) throws java.net.MalformedURLException { + int indexOfColon = path.indexOf(':'); + String scheme = path.substring(0, indexOfColon); + UCBStreamHandler handler = new UCBStreamHandler(scheme, parent.m_xSFA); + + path += UCBStreamHandler.separator; + return new URL(null, path, handler); + } + + // TODO should decide whether this should throw or not + // decide whether it should be public or protected ( final ? ) + public void loadSource() { + try { + URL sourceUrl = getSourceURL(); + + LogUtils.DEBUG("** In load source BUT not loading yet for " + + sourceUrl); + + if (sourceUrl != null) { + StringBuilder buf = new StringBuilder(); + InputStream in = sourceUrl.openStream(); + + byte[] contents = new byte[1024]; + int len; + + while ((len = in.read(contents, 0, 1024)) != -1) { + buf.append(new String(contents, 0, len)); + } + + try { + in.close(); + } catch (java.io.IOException ignore) { + LogUtils.DEBUG("** Failed to read scriot from url " + + ignore.toString()); + } + + source = buf.toString(); + hasSource = true; + } + } catch (java.io.IOException e) { + LogUtils.DEBUG("** Failed to read scriot from url " + e.toString()); + } + + } + + protected boolean writeSourceFile() { + String sourceFilePath = parent.getPathToParcel() + "/" + getLanguageName(); + boolean result = false; + + try { + + XSimpleFileAccess2 xSFA2 = + UnoRuntime.queryInterface(XSimpleFileAccess2.class, parent.m_xSFA); + + if (xSFA2 != null) { + ByteArrayInputStream bis = new ByteArrayInputStream(getSourceBytes()); + XInputStreamImpl xis = new XInputStreamImpl(bis); + xSFA2.writeFile(sourceFilePath, xis); + xis.closeInput(); + result = true; + } + } + // TODO re-examine exception processing should probably throw + // exceptions back to caller + catch (Exception ignore) { + } + + return result; + } + + protected boolean removeSourceFile() { + String parcelLocation = parent.getPathToParcel(); + String sourceFilePath = parcelLocation + "/" + getLanguageName(); + boolean result = false; + + try { + parent.m_xSFA.kill(sourceFilePath); + result = true; + } + // TODO reexamine exception handling + catch (Exception e) { + } + + return result; + } + + public URL getSourceURL() throws java.net.MalformedURLException { + String sUrl = getParcelLocation(); + sUrl = PathUtils.make_url(sUrl, getLanguageName()); + LogUtils.DEBUG("Creating script url for " + sUrl); + return createURL(sUrl); + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/UnoPkgContainer.java b/scripting/java/com/sun/star/script/framework/container/UnoPkgContainer.java new file mode 100644 index 000000000..4b6a75520 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/UnoPkgContainer.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.script.framework.container; + +import com.sun.star.deployment.XPackage; + +import com.sun.star.io.XOutputStream; +import com.sun.star.io.XTruncate; + +import com.sun.star.script.framework.io.XInputStreamWrapper; +import com.sun.star.script.framework.io.XOutputStreamWrapper; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import java.io.InputStream; +import java.io.OutputStream; + +import java.util.HashMap; +import java.util.Map; + +public class UnoPkgContainer extends ParcelContainer { + + private final Map<String, ParcelContainer> registeredPackages = new HashMap<String, ParcelContainer>(); + private final String extensionDb; + private final String extensionRepository; + + public UnoPkgContainer(XComponentContext xCtx, String locationURL, + String _extensionDb, String _extensionRepository, + String language) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + super(xCtx, locationURL, language, false); + extensionDb = _extensionDb; + extensionRepository = _extensionRepository; + init(); + } + + // gets the ParcelContainer for persisted uno packages + public ParcelContainer getRegisteredUnoPkgContainer(String url) { + + if (!url.endsWith("/")) { + url += "/"; + } + + LogUtils.DEBUG("** getRegisterPackage ctx = " + containerUrl); + LogUtils.DEBUG("** getRegisterPackage for uri " + url); + LogUtils.DEBUG("** getRegisterPackage for language " + language); + + ParcelContainer result = registeredPackages.get(url); + LogUtils.DEBUG("getRegisterPackage result is " + result); + return result; + } + + public boolean hasRegisteredUnoPkgContainer(String url) { + return getRegisteredUnoPkgContainer(url) != null; + } + + private void registerPackageContainer(String url, ParcelContainer c) { + + if (!url.endsWith("/")) { + url += "/"; + } + + LogUtils.DEBUG("RegisterPackage ctx = " + containerUrl); + LogUtils.DEBUG("RegisterPackage language = " + language); + LogUtils.DEBUG("RegisterPackage " + c + " for url " + url); + registeredPackages.put(url, c); + } + + public void deRegisterPackageContainer(String url) { + + if (!url.endsWith("/")) { + url += "/"; + } + + LogUtils.DEBUG("In deRegisterPackageContainer for " + url); + + if (hasRegisteredUnoPkgContainer(url)) { + try { + DeployedUnoPackagesDB db = getUnoPackagesDB(); + + if (db != null && db.removePackage(language, url)) { + writeUnoPackageDB(db); + ParcelContainer container = registeredPackages.get(url); + + if (!container.hasElements()) { + // When all libraries within a package bundle + // ( for this language ) are removed also + // remove the container from its parent + // Otherwise, a container ( with no containers ) + // representing the uno package bundle will + // still exist and so will get displayed + if (container.parent() != null) { + container.parent().removeChildContainer(container); + } + } + + registeredPackages.remove(url); + } + } catch (Exception e) { + //TODO revisit exception handling and exception here + //means something very wrong + LogUtils.DEBUG("***** deRegisterPackageContainer() got exception " + e); + } + } + + LogUtils.DEBUG("Leaving deRegisterPackageContainer for " + url); + } + + private void init() throws com.sun.star.lang.IllegalArgumentException { + LogUtils.DEBUG("getting container for " + containerUrl); + + try { + DeployedUnoPackagesDB db = getUnoPackagesDB(); + + if (db != null) { + String[] packages = db.getDeployedPackages(language); + + for (String thepackage : packages) { + try { + processUnoPackage(thepackage, language); + } catch (com.sun.star.lang.IllegalArgumentException ila) { + LogUtils.DEBUG("Failed to process " + thepackage + + " for " + language); + LogUtils.DEBUG(" Reason: " + ila); + } catch (Exception e) { + // TODO proper exception or do we wish + // to ignore errors here + LogUtils.DEBUG("Something very wrong!!!!!"); + LogUtils.DEBUG("Failed to process " + thepackage + + " for " + language); + LogUtils.DEBUG(" Reason: " + e); + } + } + } + } catch (com.sun.star.lang.WrappedTargetException e) { + // no deployed packages + LogUtils.DEBUG("No deployed uno-packages for " + containerUrl); + } + } + + @Override + public ScriptMetaData findScript(ParsedScriptUri psu) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + String functionName = psu.function; + String parcelName = psu.parcel; + String location = psu.location; + + LogUtils.DEBUG("*** UnoPkgContainer.findScript() ***" + + "\ncontainerUrl = " + containerUrl + + "\nfunction = " + functionName + + "\nlocation = " + location + + "\nparcel = " + parcelName); + + ParcelContainer pc = getChildContainer(location); + + if (pc == null) { + throw new com.sun.star.lang.WrappedTargetException( + "Failed to resolve script " , null, + new com.sun.star.lang.IllegalArgumentException( + "Cannot resolve script location for script = " + functionName)); + } + + return pc.findScript(psu); + } + + private DeployedUnoPackagesDB getUnoPackagesDB() throws + com.sun.star.lang.WrappedTargetException { + + InputStream is = null; + DeployedUnoPackagesDB dp = null; + + try { + + String packagesUrl = + PathUtils.make_url(extensionDb, + "/Scripts/" + extensionRepository + "-extension-desc.xml"); + + LogUtils.DEBUG("getUnoPackagesDB() looking for existing db in " + + packagesUrl); + + if (m_xSFA.exists(packagesUrl)) { + if (packagesUrl.startsWith("vnd.sun.star.tdoc")) { + // handles using XStorage directly + throw new com.sun.star.lang.WrappedTargetException( + "Can't handle documents yet"); + } + + is = new XInputStreamWrapper(m_xSFA.openFileRead(packagesUrl)); + dp = new DeployedUnoPackagesDB(is); + + try { + is.close(); + is = null; + } catch (Exception ignore) { + } + } else { + LogUtils.DEBUG("getUnoPackagesDB() " + packagesUrl + + " does not exist"); + } + } catch (Exception e) { + LogUtils.DEBUG("getUnoPackagesDB() caught Exception: " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + throw new com.sun.star.lang.WrappedTargetException(e); + } finally { + if (is != null) { + try { + is.close(); + is = null; + } catch (Exception ignore) { + } + } + } + + return dp; + } + + private void writeUnoPackageDB(DeployedUnoPackagesDB dp) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("In writeUnoPackageDB() "); + + XOutputStream xos = null; + OutputStream os = null; + + try { + + String packagesUrl = + PathUtils.make_url(extensionDb, "/Scripts/" + extensionRepository + + "-extension-desc.xml"); + + xos = m_xSFA.openFileWrite(packagesUrl); + XTruncate xTrc = UnoRuntime.queryInterface(XTruncate.class, xos); + + if (xTrc != null) { + LogUtils.DEBUG("In writeUnoPackageDB() Truncating..."); + xTrc.truncate(); + } else { + LogUtils.DEBUG("In writeUnoPackageDB() CAN'T Truncate..."); + } + + os = new XOutputStreamWrapper(xos); + dp.write(os); + + try { + os.close(); // will close xos + os = null; + } catch (Exception ignore) { + } + } catch (Exception e) { + LogUtils.DEBUG("In writeUnoPackageDB() Exception: " + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } finally { + if (os != null) { + try { + os.close(); // will close xos + os = null; + } catch (Exception ignore) { + } + } + } + } + + public void processUnoPackage(XPackage dPackage, + String language) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException, + com.sun.star.container.ElementExistException { + + LogUtils.DEBUG("** in processUnoPackage "); + + String uri = dPackage.getURL(); + + if (!uri.endsWith("/")) { + uri += "/"; + } + + LogUtils.DEBUG("** processUnoPackage getURL() -> " + uri); + LogUtils.DEBUG("** processUnoPackage getName() -> " + dPackage.getName()); + LogUtils.DEBUG("** processUnoPackage getMediaType() -> " + + dPackage.getPackageType().getMediaType()); + + try { + LogUtils.DEBUG("** processUnoPackage getDisplayName() -> " + + dPackage.getDisplayName()); + } catch (com.sun.star.deployment.ExtensionRemovedException e) { + throw new com.sun.star.lang.WrappedTargetException(e.getMessage(), this, e); + } + + processUnoPackage(uri, language); + + DeployedUnoPackagesDB db = getUnoPackagesDB(); + + if (db == null) { + try { + db = new DeployedUnoPackagesDB(); + } catch (java.io.IOException ioe) { + throw new com.sun.star.lang.WrappedTargetException(ioe); + } + } + + db.addPackage(language, uri); + writeUnoPackageDB(db); + } + + private void processUnoPackage(String uri, + String language) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException, + com.sun.star.container.ElementExistException { + + if (hasRegisteredUnoPkgContainer(uri)) { + throw new com.sun.star.container.ElementExistException( + "Already a registered uno package " + uri + " for language " + + language); + } + + LogUtils.DEBUG("processUnoPackage - URL = " + uri); + LogUtils.DEBUG("processUnoPackage - script library package"); + String parentUrl = uri; + + if (uri.contains("%2Funo_packages%2F") || + uri.contains("/uno_packages/") || + uri.contains("$UNO_USER_PACKAGES_CACHE/") || + uri.contains("$UNO_SHARED_PACKAGES_CACHE/") || + uri.contains("$BUNDLED_EXTENSIONS/")) { + + //its in a bundle need to determine the uno-package file its in + LogUtils.DEBUG("processUnoPackage - is part of a UNO bundle"); + + int index = uri.lastIndexOf('/'); + + if (uri.endsWith("/")) { + uri = uri.substring(0, index); + index = uri.lastIndexOf('/'); + } + + if (index > -1) { + parentUrl = uri.substring(0, index); + LogUtils.DEBUG("processUnoPackage - composition is contained in " + + parentUrl); + } + + ParcelContainer pkgContainer = getChildContainerForURL(parentUrl); + + if (pkgContainer == null) { + pkgContainer = + new ParcelContainer(this, m_xCtx, parentUrl, language, false); + + if (pkgContainer.loadParcel(uri) == null) { + throw new com.sun.star.lang.IllegalArgumentException( + "Couldn't load script library from composition package " + + uri + " for language " + language); + } + + addChildContainer(pkgContainer); + } else { + if (pkgContainer.loadParcel(uri) == null) { + throw new com.sun.star.lang.IllegalArgumentException( + "Couldn't load script library from composition package " + + uri + " for language " + language); + } + + } + + registerPackageContainer(uri, pkgContainer); + } else { + // stand-alone library package, e.g. not contained in + // a uno package + if (loadParcel(uri) == null) { + throw new com.sun.star.lang.IllegalArgumentException( + "Couldn't load script library package " + uri + + " for language " + language); + } + + registerPackageContainer(uri, this); + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/XMLParser.java b/scripting/java/com/sun/star/script/framework/container/XMLParser.java new file mode 100644 index 000000000..38e504ff4 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/XMLParser.java @@ -0,0 +1,30 @@ +/* + * 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.script.framework.container; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.w3c.dom.Document; + +public interface XMLParser { + Document parse(InputStream inputStream) throws IOException; + void write(Document doc, OutputStream out) throws IOException; +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/container/XMLParserFactory.java b/scripting/java/com/sun/star/script/framework/container/XMLParserFactory.java new file mode 100644 index 000000000..c34908f8c --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/XMLParserFactory.java @@ -0,0 +1,193 @@ +/* + * 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.script.framework.container; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.lang.reflect.Method; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; + +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +public class XMLParserFactory { + + private static XMLParser parser = null; + private static String officedtdurl = null; + + private XMLParserFactory() {} + + public static synchronized XMLParser getParser() { + if (parser == null) + parser = new DefaultParser(); + + return parser; + } + + public static void setOfficeDTDURL(String url) { + officedtdurl = url; + } + + private static class DefaultParser implements XMLParser { + + private final DocumentBuilderFactory factory; + + public DefaultParser() { + factory = DocumentBuilderFactory.newInstance(); + } + + public Document parse(InputStream inputStream) throws IOException { + + Document result = null; + + try { + DocumentBuilder builder = factory.newDocumentBuilder(); + InputSource is = new InputSource(inputStream); + + if (officedtdurl != null) { + is.setSystemId(officedtdurl); + } + + result = builder.parse(is); + } catch (SAXParseException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } catch (SAXException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } catch (ParserConfigurationException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + + return result; + } + + public void write(Document doc, OutputStream out) throws IOException { + + Class<?> clazz = doc.getClass(); + String name = clazz.getName(); + + // depending on the class of the Document object use introspection + // to invoke the appropriate methods for writing the XML + // this code is based on the code used by the NetBeans + // class XMLUtilImpl in the openide module + try { + if (name.equals("com.sun.xml.tree.XmlDocument") || + name.equals("org.apache.crimson.tree.XmlDocument")) { + + // these DOM implementations are self writing + Method write; + + write = clazz.getDeclaredMethod("write", + new Class[] {OutputStream.class}); + + write.invoke(doc, new Object[] {out}); + } else { + // try xerces serialize package using introspection + ClassLoader cl = this.getClass().getClassLoader(); + + Class<?> serializerClass = null; + Class<?> formatterClass = null; + + try { + + serializerClass = + Class.forName("org.apache.xml.serialize.XMLSerializer", + true, cl); + + formatterClass = + Class.forName("org.apache.xml.serialize.OutputFormat", + true, cl); + + } catch (ClassNotFoundException cnfe) { + String prefix = "com.sun.org.apache.xml.internal."; + + serializerClass = + Class.forName(prefix + "serialize.XMLSerializer", + true, cl); + + formatterClass = + Class.forName(prefix + "serialize.OutputFormat", + true, cl); + } + + Object serializerObject = serializerClass.newInstance(); + Object formatterObject = formatterClass.newInstance(); + + // improve output readability using the OutputFormat class + Method method = + formatterClass.getMethod("setMethod", + new Class[] {String.class}); + + method.invoke(formatterObject, new Object[] {"xml"}); + + method = formatterClass.getMethod("setIndenting", + new Class[] {Boolean.TYPE}); + + method.invoke(formatterObject, new Object[] {Boolean.TRUE}); + + // now set up an instance of XMLSerializer with our + // OutputStream and serialize our Document + method = serializerClass.getMethod("setOutputByteStream", + new Class[] {OutputStream.class}); + + method.invoke(serializerObject, new Object[] {out}); + + method = serializerClass.getMethod("setOutputFormat", + new Class[] {formatterClass}); + + method.invoke(serializerObject, new Object[] {formatterObject}); + + method = serializerClass.getMethod("asDOMSerializer", new Class[0]); + + Object impl = method.invoke(serializerObject, new Object[0]); + + method = impl.getClass().getMethod("serialize", + new Class[] {Document.class}); + + method.invoke(impl, new Object[] {doc}); + } + } catch (NoSuchMethodException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } catch (ClassNotFoundException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } catch (Exception ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/io/UCBStreamHandler.java b/scripting/java/com/sun/star/script/framework/io/UCBStreamHandler.java new file mode 100644 index 000000000..6413745d3 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/io/UCBStreamHandler.java @@ -0,0 +1,275 @@ +/* + * 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.script.framework.io; + +import com.sun.star.io.XInputStream; +import com.sun.star.io.XOutputStream; +import com.sun.star.io.XTruncate; + +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.ucb.XSimpleFileAccess; + +import com.sun.star.uno.UnoRuntime; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +import java.util.HashMap; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class UCBStreamHandler extends URLStreamHandler { + + public static final String separator = "/ucb/"; + + private final XSimpleFileAccess m_xSimpleFileAccess; + private final HashMap<String, InputStream> m_jarStreamMap = new HashMap<String, InputStream>(12); + private static String m_ucbscheme; + + public UCBStreamHandler(String scheme, XSimpleFileAccess xSFA) { + LogUtils.DEBUG("UCBStreamHandler ctor, scheme = " + scheme); + UCBStreamHandler.m_ucbscheme = scheme; + this.m_xSimpleFileAccess = xSFA; + } + + @Override + public void parseURL(URL url, String spec, int start, int limit) { + + LogUtils.DEBUG("**XUCBStreamHandler, parseURL: " + url + " spec: " + + spec + " start: " + start + " limit: " + limit); + + String file = url.getFile(); + + if (file == null) + file = spec.substring(start, limit); + else + file += spec.substring(start, limit); + + LogUtils.DEBUG("**For scheme = " + m_ucbscheme); + LogUtils.DEBUG("**Setting path = " + file); + setURL(url, m_ucbscheme, null, -1, null, null, file, null, null); + } + + @Override + public URLConnection openConnection(URL u) throws IOException { + return new UCBConnection(u); + } + + private class UCBConnection extends URLConnection { + + public UCBConnection(URL url) { + super(url); + } + + @Override + public void connect() { + } + + @Override + public InputStream getInputStream() throws IOException { + LogUtils.DEBUG("UCBConnectionHandler GetInputStream on " + url); + String sUrl = url.toString(); + + if (sUrl.lastIndexOf(separator) == -1) { + LogUtils.DEBUG("getInputStream straight file load"); + return getFileStreamFromUCB(sUrl); + } else { + String path = sUrl.substring(0, sUrl.lastIndexOf(separator)); + + String file = + sUrl.substring(sUrl.lastIndexOf(separator) + separator.length()); + + LogUtils.DEBUG("getInputStream, load of file from another file eg. " + + file + " from " + path); + + return getUCBStream(file, path); + } + } + @Override + public OutputStream getOutputStream() throws IOException { + LogUtils.DEBUG("UCBConnectionHandler getOutputStream on " + url); + OutputStream os = null; + + try { + String sUrl = url.toString(); + + if (sUrl.lastIndexOf(separator) != -1) { + String path = sUrl.substring(0, sUrl.lastIndexOf(separator)); + + if (m_xSimpleFileAccess.isReadOnly(path)) { + throw new java.io.IOException("File is read only"); + } + + LogUtils.DEBUG("getOutputStream, create o/p stream for file eg. " + + path); + + // we will only deal with simple file write + XOutputStream xos = m_xSimpleFileAccess.openFileWrite(path); + + XTruncate xtrunc = + UnoRuntime.queryInterface(XTruncate.class, xos); + + if (xtrunc != null) { + xtrunc.truncate(); + } + + os = new XOutputStreamWrapper(xos); + } + + if (os == null) { + throw new IOException("Failed to get OutputStream for " + + sUrl); + } + } catch (com.sun.star.ucb.CommandAbortedException cae) { + LogUtils.DEBUG("caught exception: " + cae.toString() + + " getting writable stream from " + url); + IOException newEx = new IOException(cae.getMessage()); + newEx.initCause(cae); + throw newEx; + } catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG("caught unknown exception: " + e.toString() + + " getting writable stream from " + url); + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + + return os; + } + } + + private InputStream getUCBStream(String file, String path) throws IOException { + + InputStream is = null; + InputStream result = null; + + try { + if (path.endsWith(".jar")) { + is = m_jarStreamMap.get(path); + + if (is == null) { + is = getFileStreamFromUCB(path); + m_jarStreamMap.put(path, is); + } else { + try { + is.reset(); + } catch (IOException e) { + is.close(); + is = getFileStreamFromUCB(path); + m_jarStreamMap.put(path, is); + } + } + + result = getFileStreamFromJarStream(file, is); + } else { + String fileUrl = PathUtils.make_url(path, file); + result = getFileStreamFromUCB(fileUrl); + } + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException ioe) { + LogUtils.DEBUG("Caught exception closing stream: " + + ioe.getMessage()); + } + } + } + + return result; + } + + private InputStream getFileStreamFromJarStream(String file, + InputStream is) throws + IOException { + + ZipInputStream zis = new ZipInputStream(is); + + while (zis.available() != 0) { + ZipEntry entry = zis.getNextEntry(); + + if (entry != null && entry.getName().equals(file)) { + return zis; + } + } + + return null; + } + + private InputStream getFileStreamFromUCB(String path) throws IOException { + + InputStream result = null; + XInputStream xInputStream = null; + + try { + LogUtils.DEBUG("Trying to read from " + path); + xInputStream = m_xSimpleFileAccess.openFileRead(path); + LogUtils.DEBUG("sfa appeared to read file "); + byte[][] inputBytes = new byte[1][]; + + int sz = m_xSimpleFileAccess.getSize(path); + + // TODO don't depend on result of available() or size() + // just read stream 'till complete + if (sz == 0 && xInputStream.available() > 0) { + sz = xInputStream.available(); + } + + LogUtils.DEBUG("size of file " + path + " is " + sz); + LogUtils.DEBUG("available = " + xInputStream.available()); + inputBytes[0] = new byte[sz]; + + int ln = xInputStream.readBytes(inputBytes, sz); + + if (ln != sz) { + throw new IOException( + "Failed to read " + sz + " bytes from XInputStream"); + } + + result = new ByteArrayInputStream(inputBytes[0]); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } catch (com.sun.star.uno.Exception ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } finally { + if (xInputStream != null) { + try { + xInputStream.closeInput(); + } catch (Exception e2) { + LogUtils.DEBUG("Error closing XInputStream: " + + e2.getMessage()); + } + } + } + + return result; + } +} diff --git a/scripting/java/com/sun/star/script/framework/io/XInputStreamImpl.java b/scripting/java/com/sun/star/script/framework/io/XInputStreamImpl.java new file mode 100644 index 000000000..8320b6227 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/io/XInputStreamImpl.java @@ -0,0 +1,109 @@ +/* + * 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.script.framework.io; + +import com.sun.star.io.XInputStream; + +import java.io.IOException; +import java.io.InputStream; + +public class XInputStreamImpl implements XInputStream { + + private final InputStream is; + + public XInputStreamImpl(InputStream is) { + this.is = is; + } + + public int readBytes(/*OUT*/byte[][] aData, /*IN*/int nBytesToRead) throws + com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + + aData[ 0 ] = new byte[ nBytesToRead ]; + int totalBytesRead = 0; + + try { + int bytesRead; + + while ((bytesRead = is.read(aData[ 0 ], totalBytesRead, nBytesToRead)) > 0 + && (totalBytesRead < nBytesToRead)) { + totalBytesRead += bytesRead; + nBytesToRead -= bytesRead; + } + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } catch (IndexOutOfBoundsException aie) { + throw new com.sun.star.io.BufferSizeExceededException(aie); + } + + return totalBytesRead; + } + + public int readSomeBytes(/*OUT*/byte[][] aData, /*IN*/int nMaxBytesToRead) + throws com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + + int bytesToRead = nMaxBytesToRead; + int availableBytes = available(); + + if (availableBytes < nMaxBytesToRead) { + bytesToRead = availableBytes; + } + + int read = readBytes(aData, bytesToRead); + return read; + } + + public void skipBytes(/*IN*/int nBytesToSkip) throws + com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + + try { + do { + nBytesToSkip -= is.skip(nBytesToSkip); + } while (nBytesToSkip > 0); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + } + + public int available() throws + com.sun.star.io.NotConnectedException, com.sun.star.io.IOException { + + int bytesAvail = 0; + + try { + bytesAvail = is.available(); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + + return bytesAvail; + } + + public void closeInput() throws + com.sun.star.io.NotConnectedException, com.sun.star.io.IOException { + + try { + is.close(); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/io/XInputStreamWrapper.java b/scripting/java/com/sun/star/script/framework/io/XInputStreamWrapper.java new file mode 100644 index 000000000..654f0e24c --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/io/XInputStreamWrapper.java @@ -0,0 +1,100 @@ +/* + * 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.script.framework.io; + +import com.sun.star.io.XInputStream; + +import java.io.IOException; +import java.io.InputStream; + +public class XInputStreamWrapper extends InputStream { + + private final XInputStream m_xInputStream; + + public XInputStreamWrapper(XInputStream xInputStream) { + m_xInputStream = xInputStream; + } + + @Override + public int read() throws java.io.IOException { + byte[][] byteRet = new byte[1][0]; + long numRead; + + try { + numRead = m_xInputStream.readBytes(byteRet, 1); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + + if (numRead != 1) { + return -1; + } + + return byteRet[0][0]; + } + + @Override + public int read(byte[] b) throws java.io.IOException { + byte[][] byteRet = new byte[1][]; + byteRet[0] = b; + + try { + return m_xInputStream.readBytes(byteRet, b.length); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public long skip(long n) throws java.io.IOException { + try { + m_xInputStream.skipBytes((int)n); + return n; + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public int available() throws java.io.IOException { + try { + return m_xInputStream.available(); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public void close() throws java.io.IOException { + try { + m_xInputStream.closeInput(); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/io/XOutputStreamWrapper.java b/scripting/java/com/sun/star/script/framework/io/XOutputStreamWrapper.java new file mode 100644 index 000000000..16357fd17 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/io/XOutputStreamWrapper.java @@ -0,0 +1,113 @@ +/* + * 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.script.framework.io; + +import com.sun.star.io.XOutputStream; + +import java.io.IOException; +import java.io.OutputStream; + +public class XOutputStreamWrapper extends OutputStream { + + private final XOutputStream m_xOutputStream; + + public XOutputStreamWrapper(XOutputStream xOs) { + this.m_xOutputStream = xOs; + } + + @Override + public void write(int b) throws java.io.IOException { + if (m_xOutputStream == null) { + throw new java.io.IOException("Stream is null"); + } + + byte[] bytes = new byte[1]; + bytes[0] = (byte) b; + + try { + m_xOutputStream.writeBytes(bytes); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public void write(byte[] b) throws java.io.IOException { + + if (m_xOutputStream == null) { + throw new java.io.IOException("Stream is null"); + } + + try { + m_xOutputStream.writeBytes(b); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + @Override + public void write(byte[] b, int off, int len) throws java.io.IOException { + if (m_xOutputStream == null) { + throw new java.io.IOException("Stream is null"); + } + + byte[] bytes = new byte[len]; + System.arraycopy(b, off, bytes, 0, len); + + try { + m_xOutputStream.writeBytes(bytes); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public void flush() throws java.io.IOException { + if (m_xOutputStream == null) { + throw new java.io.IOException("Stream is null"); + } + + try { + m_xOutputStream.flush(); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public void close() throws java.io.IOException { + if (m_xOutputStream == null) { + throw new java.io.IOException("Stream is null"); + } + + try { + m_xOutputStream.closeOutput(); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/io/XStorageHelper.java b/scripting/java/com/sun/star/script/framework/io/XStorageHelper.java new file mode 100644 index 000000000..79c5f10bc --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/io/XStorageHelper.java @@ -0,0 +1,249 @@ +/* + * 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.script.framework.io; + +import com.sun.star.beans.XPropertySet; + +import com.sun.star.container.XNameAccess; + +import com.sun.star.document.XDocumentSubStorageSupplier; + +import com.sun.star.embed.XStorage; +import com.sun.star.embed.XTransactedObject; + +import com.sun.star.frame.XModel; + +import com.sun.star.lang.EventObject; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; + +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + +public class XStorageHelper implements XEventListener { + + XStorage[] xStorages; + static Map<String, XModel> modelMap = new HashMap<String, XModel>(); + XModel xModel = null; + private static XStorageHelper listener = new XStorageHelper(); + + private XStorageHelper() {} + + public XStorageHelper(String path, int mode, + boolean create) throws IOException { + + String modelUrl = null; + int indexOfScriptsDir = path.lastIndexOf("Scripts"); + + if (indexOfScriptsDir > -1) { + modelUrl = path.substring(0, indexOfScriptsDir - 1); + path = path.substring(indexOfScriptsDir, path.length()); + } + + LogUtils.DEBUG("XStorageHelper ctor, path: " + path); + this.xModel = getModelForURL(modelUrl); + + try { + StringTokenizer tokens = new StringTokenizer(path, "/"); + + if (tokens.countTokens() == 0) { + throw new IOException("Invalid path"); + } + + XDocumentSubStorageSupplier xDocumentSubStorageSupplier = + UnoRuntime.queryInterface(XDocumentSubStorageSupplier.class, + xModel); + + xStorages = new XStorage[tokens.countTokens()]; + + LogUtils.DEBUG("XStorageHelper ctor, path chunks length: " + + xStorages.length); + + for (int i = 0; i < xStorages.length; i++) { + LogUtils.DEBUG("XStorageHelper, processing index " + i); + String name = tokens.nextToken(); + LogUtils.DEBUG("XStorageHelper, getting: " + name); + XStorage storage = null; + + if (i == 0) { + storage = xDocumentSubStorageSupplier.getDocumentSubStorage(name, mode); + + if (storage == null) { + LogUtils.DEBUG("** boo hoo Storage is null "); + } + + XPropertySet xProps = + UnoRuntime.queryInterface(XPropertySet.class, storage); + + if (xProps != null) { + + String mediaType = + AnyConverter.toString(xProps.getPropertyValue("MediaType")); + + LogUtils.DEBUG("***** media type is " + mediaType); + + if (!mediaType.equals("scripts")) { + xProps.setPropertyValue("MediaType", "scripts"); + } + } + } else { + + XNameAccess xNameAccess = + UnoRuntime.queryInterface(XNameAccess.class, xStorages[i - 1]); + + if (xNameAccess == null) { + disposeObject(); + throw new IOException("No name access " + name); + } else if (!xNameAccess.hasByName(name) + || !xStorages[i - 1].isStorageElement(name)) { + if (!create) { + disposeObject(); + throw new IOException("No subdir: " + name); + } else { + // attempt to create new storage + LogUtils.DEBUG("Attempt to create new storage for " + + name); + } + } + + storage = xStorages[i - 1].openStorageElement( + name, mode); + } + + if (storage == null) { + disposeObject(); + throw new IOException("storage not found: " + name); + } + + xStorages[i] = storage; + + } + } catch (com.sun.star.io.IOException ioe) { + disposeObject(); + } catch (com.sun.star.uno.Exception ex1) { + disposeObject(); + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + public synchronized static void addNewModel(XModel model) { + // TODO needs to cater for model for untitled document + modelMap.put(PathUtils.getOidForModel(model), model); + XComponent xComp = UnoRuntime.queryInterface(XComponent.class, model); + + if (xComp != null) { + try { + xComp.addEventListener(listener); + } catch (Exception e) { + // What TODO here ? + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + } + } + + public void disposing(EventObject Source) { + XModel model = UnoRuntime.queryInterface(XModel.class, Source.Source); + + if (model != null) { + LogUtils.DEBUG(" Disposing doc " + model.getURL()); + modelMap.remove(PathUtils.getOidForModel(model)); + } + } + + public XStorage getStorage() { + return xStorages[ xStorages.length - 1 ]; + } + + public XModel getModel() { + return xModel; + } + + public void disposeObject() { + disposeObject(false); + } + + public void disposeObject(boolean shouldCommit) { + LogUtils.DEBUG("In disposeObject"); + + for (int i = xStorages.length - 1 ; i > -1; i--) { + LogUtils.DEBUG("In disposeObject disposing storage " + i); + + try { + XStorage xStorage = xStorages[i]; + + if (shouldCommit) { + commit(xStorage); + } + + disposeObject(xStorage); + LogUtils.DEBUG("In disposeObject disposed storage " + i); + } catch (Exception ignore) { + LogUtils.DEBUG("Exception disposing storage " + i); + } + + } + + } + + public static void disposeObject(XInterface xInterface) { + if (xInterface == null) { + return; + } + + XComponent xComponent = + UnoRuntime.queryInterface(XComponent.class, xInterface); + + if (xComponent == null) { + return; + } + + xComponent.dispose(); + } + + public static void commit(XInterface xInterface) { + + XTransactedObject xTrans = + UnoRuntime.queryInterface(XTransactedObject.class, xInterface); + + if (xTrans != null) { + try { + xTrans.commit(); + } catch (Exception e) { + LogUtils.DEBUG("Something went belly up exception: " + e); + } + } + } + + public XModel getModelForURL(String url) { + //TODO does not cater for untitled documents + return modelMap.get(url); + } +} diff --git a/scripting/java/com/sun/star/script/framework/log/LogUtils.java b/scripting/java/com/sun/star/script/framework/log/LogUtils.java new file mode 100644 index 000000000..cd42b9d1e --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/log/LogUtils.java @@ -0,0 +1,57 @@ +/* + * 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.script.framework.log; + +import java.io.StringWriter; +import java.io.PrintWriter; + +public class LogUtils { + + private static boolean m_bDebugEnabled = false; + + static { + String debugFlag = + System.getProperties().getProperty("ScriptJavaRuntimeDebug"); + + if (debugFlag != null && debugFlag.length() > 0) { + m_bDebugEnabled = debugFlag.equalsIgnoreCase("true"); + } + } + + // Ensure that instances of this class cannot be created + private LogUtils() { + } + + /** + * Print Debug Output + * + * @param msg message to be displayed + */ + public static void DEBUG(String msg) { + if (m_bDebugEnabled) { + System.out.println(msg); + } + } + + public static String getTrace(Exception e) { + StringWriter w = new StringWriter(); + e.printStackTrace(new PrintWriter(w)); + return w.toString(); + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/ClassLoaderFactory.java b/scripting/java/com/sun/star/script/framework/provider/ClassLoaderFactory.java new file mode 100644 index 000000000..0974b8874 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/ClassLoaderFactory.java @@ -0,0 +1,55 @@ +/* + * 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.script.framework.provider; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; + +import java.net.URL; +import java.net.URLClassLoader; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * Class Loader Factory + */ +public class ClassLoaderFactory { + + private ClassLoaderFactory() {} + + public static URLClassLoader getURLClassLoader(ScriptMetaData scriptData) { + ClassLoader parent = scriptData.getClass().getClassLoader(); + URL[] classPath = scriptData.getClassPath(); + LogUtils.DEBUG("Classpath has length " + classPath.length); + + for (int i = 0; i < classPath.length; i++) { + LogUtils.DEBUG("ClassPath " + i + "} is " + classPath[ i ].toString()); + } + + return getURLClassLoader(parent, classPath); + } + + public static URLClassLoader getURLClassLoader(final ClassLoader parent, + final URL[] classpath) { + return AccessController.doPrivileged( + new PrivilegedAction<URLClassLoader>() { + public URLClassLoader run() { return new URLClassLoader(classpath, parent); } + }); + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/EditorScriptContext.java b/scripting/java/com/sun/star/script/framework/provider/EditorScriptContext.java new file mode 100644 index 000000000..d107edcc7 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/EditorScriptContext.java @@ -0,0 +1,79 @@ +/* + * 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.script.framework.provider; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XDesktop; +import com.sun.star.frame.XModel; + +import com.sun.star.script.provider.XScriptContext; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +public class EditorScriptContext implements XScriptContext { + + private final XDesktop m_xDeskTop; + private final XComponentContext m_xComponentContext; + + public EditorScriptContext(XComponentContext xmComponentContext, + XDesktop xDesktop) { + this.m_xComponentContext = xmComponentContext; + this.m_xDeskTop = xDesktop; + } + + /** + Obtain the document reference on which the script can operate + + @return XModel interface + */ + public XModel getDocument() { + + XModel xModel = + UnoRuntime.queryInterface(XModel.class, m_xDeskTop.getCurrentComponent()); + + return xModel; + } + + public XScriptInvocationContext getInvocationContext() { + + XScriptInvocationContext xContext = + UnoRuntime.queryInterface(XScriptInvocationContext.class, getDocument()); + + return xContext; + } + + /** + Obtain the desktop reference on which the script can operate + + @return XDesktop interface + */ + public XDesktop getDesktop() { + return m_xDeskTop; + } + + /** + Obtain the component context which the script can use to create other uno components + + @return XComponentContext interface + */ + public XComponentContext getComponentContext() { + return m_xComponentContext; + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/PathUtils.java b/scripting/java/com/sun/star/script/framework/provider/PathUtils.java new file mode 100644 index 000000000..08b859f4e --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/PathUtils.java @@ -0,0 +1,83 @@ +/* + * 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.script.framework.provider; + +import com.sun.star.frame.XModel; +import com.sun.star.uno.IQueryInterface; + +import java.lang.reflect.Method; + +import java.util.StringTokenizer; + +public class PathUtils { + + public static String BOOTSTRAP_NAME; + private static boolean m_windows = false; + + static { + // detect if windows platform + if (System.getProperty("os.name").startsWith("Windows")) + m_windows = true; + + BOOTSTRAP_NAME = m_windows ? "bootstrap.ini" : "bootstraprc"; + } + + public static String getOidForModel(XModel xModel) { + String oid = ""; + + if (xModel != null) { + try { + + Method getOid = + IQueryInterface.class.getMethod("getOid", (java.lang.Class[])null); + + if (getOid != null) { + oid = (String)getOid.invoke(xModel, new Object[0]); + } + + } catch (Exception ignore) { + } + } + + return oid; + } + + static public String make_url(String baseUrl, String url) { + StringBuilder buff = new StringBuilder(baseUrl.length() + url.length()); + buff.append(baseUrl); + StringTokenizer t = new StringTokenizer(url, "/"); + + while (t.hasMoreElements()) { + if (buff.charAt(buff.length() - 1) != '/') { + buff.append('/'); + } + + try { + buff.append(java.net.URLEncoder.encode((String)t.nextElement(), "UTF-8")); + } catch (java.io.UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + return buff.toString(); + } + + private PathUtils() { + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/ScriptContext.java b/scripting/java/com/sun/star/script/framework/provider/ScriptContext.java new file mode 100644 index 000000000..1f73ffbd4 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/ScriptContext.java @@ -0,0 +1,143 @@ +/* + * 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.script.framework.provider; + +import com.sun.star.beans.PropertyAttribute; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XDesktop; +import com.sun.star.frame.XModel; + +import com.sun.star.lang.XMultiComponentFactory; + +import com.sun.star.lib.uno.helper.PropertySet; + +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.provider.XScriptContext; + +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +public class ScriptContext extends PropertySet implements XScriptContext { + + private static final String HM_DOC_REF = "DocumentReference"; + private static final String HM_DESKTOP = "Desktop"; + private static final String HM_COMPONENT_CONTEXT = "ComponentContext"; + + private static final String DOC_URI = "SCRIPTING_DOC_URI"; + + public final XModel m_xModel; + private final XScriptInvocationContext m_xInvocationContext; + + private final XDesktop m_xDeskTop; + + private final XComponentContext m_xComponentContext; + + // property, accessed via reflection + public String m_sDocURI = null; + + private ScriptContext(XComponentContext xmComponentContext, XDesktop xDesktop, + XModel xModel, XScriptInvocationContext xInvocContext) { + + this.m_xDeskTop = xDesktop; + this.m_xComponentContext = xmComponentContext; + this.m_xModel = xModel; + this.m_xInvocationContext = xInvocContext; + + if (m_xModel != null) { + + registerProperty(DOC_URI, new Type(String.class), + (short)(PropertyAttribute.MAYBEVOID | PropertyAttribute.TRANSIENT), + "m_sDocURI"); + + } + + registerProperty(HM_DOC_REF, new Type(XModel.class), + (short)(PropertyAttribute.MAYBEVOID | PropertyAttribute.TRANSIENT), + "m_xModel"); + + registerProperty(HM_DESKTOP, new Type(XDesktop.class), + (short)(PropertyAttribute.MAYBEVOID | PropertyAttribute.TRANSIENT), + "m_xDeskTop"); + + registerProperty(HM_COMPONENT_CONTEXT, new Type(XDesktop.class), + (short)(PropertyAttribute.MAYBEVOID | PropertyAttribute.TRANSIENT), + "m_xComponentContext"); + } + + public static XScriptContext createContext(XModel xModel, + XScriptInvocationContext xInvocContext, + XComponentContext xCtxt, XMultiComponentFactory xMCF) { + + XScriptContext sc = null; + + try { + + Object xInterface = + xMCF.createInstanceWithContext("com.sun.star.frame.Desktop", xCtxt); + + XDesktop xDesktop = UnoRuntime.queryInterface(XDesktop.class, xInterface); + + if (xModel != null) { + sc = new ScriptContext(xCtxt, xDesktop, xModel, xInvocContext); + } else { + sc = new EditorScriptContext(xCtxt, xDesktop); + } + + } catch (Exception e) { + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + + return sc; + } + + + /** + Obtain the document reference on which the script can operate + + @return XModel interface + */ + public XModel getDocument() { + return m_xModel; + } + + public XScriptInvocationContext getInvocationContext() { + return m_xInvocationContext; + } + + /** + Obtain the desktop reference on which the script can operate + + @return XDesktop interface + */ + public XDesktop getDesktop() { + return m_xDeskTop; + } + + /** + Obtain the component context which the script can use to create other uno components + + @return XComponentContext interface + */ + public XComponentContext getComponentContext() { + return m_xComponentContext; + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/ScriptEditor.java b/scripting/java/com/sun/star/script/framework/provider/ScriptEditor.java new file mode 100644 index 000000000..dbcb3b0bc --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/ScriptEditor.java @@ -0,0 +1,79 @@ +/* + * 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.script.framework.provider; + +import com.sun.star.beans.NamedValue; +import com.sun.star.configuration.theDefaultProvider; +import com.sun.star.container.XNameAccess; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.provider.XScriptContext; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.UnoRuntime; + +import javax.swing.JOptionPane; +import javax.swing.JDialog; + +public abstract class ScriptEditor { + public XScriptContext context; + + public abstract Object execute() throws Exception; + public abstract void indicateErrorLine(int lineNum); + public abstract void edit(XScriptContext context, ScriptMetaData entry); + public abstract String getTemplate(); + public abstract String getExtension(); + + public void setContext(XScriptContext context) { + this.context = context; + } + + public boolean isMacroExectionEnabled() { + XNameAccess xNameAccess = null; + try { + String sAccess = "com.sun.star.configuration.ConfigurationAccess"; + XMultiServiceFactory xMSFCfg = theDefaultProvider.get(context.getComponentContext()); + Object oAccess = xMSFCfg.createInstanceWithArguments(sAccess, + new Object[] { new NamedValue("nodepath", "org.openoffice.Office.Common/Security/Scripting") }); + xNameAccess = UnoRuntime.queryInterface(XNameAccess.class, oAccess); + Object result = xNameAccess.getByName("DisableMacrosExecution"); + boolean bMacrosDisabled = AnyConverter.toBoolean(result); + if (bMacrosDisabled) + return false; + } catch (com.sun.star.uno.Exception e) { + return false; + } + return true; + } + + // Wraps long error messages + private static class NarrowOptionPane extends JOptionPane { + private static final long serialVersionUID = 1L; + public int getMaxCharactersPerLineCount() { + return 100; + } + } + + public void showErrorMessage(String message) { + JOptionPane optionPane = new NarrowOptionPane(); + optionPane.setMessage(message); + optionPane.setMessageType(JOptionPane.ERROR_MESSAGE); + JDialog dialog = optionPane.createDialog(null, "Error"); + dialog.setVisible(true); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/ScriptProvider.java b/scripting/java/com/sun/star/script/framework/provider/ScriptProvider.java new file mode 100644 index 000000000..e48bc48fb --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/ScriptProvider.java @@ -0,0 +1,673 @@ +/* + * 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.script.framework.provider; + +import com.sun.star.beans.Property; +import com.sun.star.beans.XIntrospectionAccess; +import com.sun.star.beans.XPropertyChangeListener; +import com.sun.star.beans.XPropertySet; +import com.sun.star.beans.XPropertySetInfo; +import com.sun.star.beans.XVetoableChangeListener; + +import com.sun.star.container.XNameContainer; + +import com.sun.star.deployment.XPackage; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XModel; +import com.sun.star.frame.XTransientDocumentsDocumentContentFactory; + +import com.sun.star.lang.XInitialization; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XTypeProvider; + +import com.sun.star.script.XInvocation; +import com.sun.star.script.browse.BrowseNodeTypes; +import com.sun.star.script.browse.XBrowseNode; +import com.sun.star.script.framework.browse.DialogFactory; +import com.sun.star.script.framework.browse.ProviderBrowseNode; +import com.sun.star.script.framework.container.ParcelContainer; +import com.sun.star.script.framework.container.ParsedScriptUri; +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.container.UnoPkgContainer; +import com.sun.star.script.framework.container.XMLParserFactory; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.provider.ScriptFrameworkErrorException; +import com.sun.star.script.provider.ScriptFrameworkErrorType; +import com.sun.star.script.provider.XScript; +import com.sun.star.script.provider.XScriptContext; +import com.sun.star.script.provider.XScriptProvider; + +import com.sun.star.sdbc.XRow; + +import com.sun.star.ucb.Command; +import com.sun.star.ucb.UniversalContentBroker; +import com.sun.star.ucb.XCommandProcessor; +import com.sun.star.ucb.XContent; +import com.sun.star.ucb.XContentIdentifier; +import com.sun.star.ucb.XUniversalContentBroker; + +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Exception; +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.util.XMacroExpander; + +public abstract class ScriptProvider implements + XScriptProvider, XBrowseNode, XPropertySet, XInvocation, XInitialization, + XTypeProvider, XServiceInfo, XNameContainer { + + private final String[] __serviceNames = { + "com.sun.star.script.provider.ScriptProviderFor", + "com.sun.star.script.provider.LanguageScriptProvider", + "com.sun.star.script.browse.BrowseNode", + "com.sun.star.script.provider.ScriptProvider" + }; + + protected String language; + + protected XComponentContext m_xContext; + private XMultiComponentFactory m_xMultiComponentFactory; + protected XModel m_xModel; + protected XScriptInvocationContext m_xInvocContext; + private ParcelContainer m_container; + + // proxies to helper objects which implement interfaces + private XPropertySet m_xPropertySetProxy; + private XInvocation m_xInvocationProxy; + + // TODO should this be implemented in this class + private XBrowseNode m_xBrowseNodeProxy; + private XScriptContext m_xScriptContext; + + public ScriptProvider(XComponentContext ctx, String language) { + this.language = language; + __serviceNames[0] += language; + + LogUtils.DEBUG("ScriptProvider: constructor - start. " + language); + + m_xContext = ctx; + + // Initialize DialogFactory class in case dialogs are required + DialogFactory.createDialogFactory(m_xContext); + + try { + m_xMultiComponentFactory = m_xContext.getServiceManager(); + + if (m_xMultiComponentFactory == null) { + throw new Exception("Error could not obtain a " + + "multicomponent factory - rethrowing Exception."); + } + + Object serviceObj = + m_xContext.getValueByName( + "/singletons/com.sun.star.util.theMacroExpander"); + + XMacroExpander me = + (XMacroExpander) AnyConverter.toObject( + new Type(XMacroExpander.class), serviceObj); + + XMLParserFactory.setOfficeDTDURL( + me.expandMacros( + "$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/dtd/officedocument/1_0/")); + + } catch (Exception e) { + LogUtils.DEBUG(LogUtils.getTrace(e)); + throw new com.sun.star.uno.RuntimeException( + e, "Error constructing ScriptProvider: " + e); + } + + LogUtils.DEBUG("ScriptProvider: constructor - finished."); + } + + synchronized public XScriptContext getScriptingContext() { + if (m_xScriptContext == null) { + m_xScriptContext = ScriptContext.createContext(m_xModel, m_xInvocContext, + m_xContext, m_xMultiComponentFactory); + } + + return m_xScriptContext; + } + + public void initialize(Object[] aArguments) + throws com.sun.star.uno.Exception { + LogUtils.DEBUG("entering XInit for language " + language); + boolean isPkgProvider = false; + + if (aArguments.length == 1) { + String contextUrl = null; + + if (AnyConverter.getType(aArguments[0]).getTypeClass().equals( + TypeClass.INTERFACE)) { + // try whether it denotes a XScriptInvocationContext + m_xInvocContext = + UnoRuntime.queryInterface(XScriptInvocationContext.class, aArguments[0]); + + if (m_xInvocContext != null) { + // if so, obtain the document - by definition, this must be + // the ScriptContainer + m_xModel = + UnoRuntime.queryInterface(XModel.class, m_xInvocContext.getScriptContainer()); + } else { + // otherwise, check whether it's an XModel + m_xModel = + UnoRuntime.queryInterface(XModel.class, aArguments[0]); + } + + if (m_xModel == null) { + throw new com.sun.star.uno.Exception( + "ScriptProvider argument must be either a string, a valid XScriptInvocationContext, " + + "or an XModel", this); + } + + contextUrl = getDocUrlFromModel(m_xModel); + m_container = new ParcelContainer(m_xContext, contextUrl, language); + + } else if (AnyConverter.isString(aArguments[0])) { + + String originalContextURL = AnyConverter.toString(aArguments[0]); + LogUtils.DEBUG("creating Application, path: " + originalContextURL); + contextUrl = originalContextURL; + + // TODO no support for packages in documents yet + if (originalContextURL.startsWith("vnd.sun.star.tdoc")) { + m_container = new ParcelContainer(m_xContext, contextUrl, language); + m_xModel = getModelFromDocUrl(originalContextURL); + } else { + String extensionDb = "vnd.sun.star.expand:${$BRAND_INI_DIR/" + + PathUtils.BOOTSTRAP_NAME + "::UserInstallation}/user"; + + String extensionRepository = null; + + if (originalContextURL.startsWith("bundled")) { + contextUrl = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS"; + extensionRepository = "bundled"; + } else if (originalContextURL.startsWith("share")) { + contextUrl = "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR"; + extensionRepository = "shared"; + } else if (originalContextURL.startsWith("user")) { + contextUrl = "vnd.sun.star.expand:${$BRAND_INI_DIR/" + + PathUtils.BOOTSTRAP_NAME + "::UserInstallation}/user"; + extensionRepository = "user"; + } + + if (originalContextURL.endsWith("uno_packages")) { + isPkgProvider = true; + + if (!originalContextURL.equals(contextUrl) + && extensionRepository != null + && !extensionRepository.equals("bundled")) { + + contextUrl = PathUtils.make_url(contextUrl, "uno_packages"); + } + } + + if (isPkgProvider) { + m_container = + new UnoPkgContainer(m_xContext, contextUrl, extensionDb, extensionRepository, + language); + } else { + m_container = + new ParcelContainer(m_xContext, contextUrl, language); + } + } + } else { + throw new com.sun.star.uno.RuntimeException( + "ScriptProvider created with invalid argument"); + } + + LogUtils.DEBUG("Modified Application path is: " + contextUrl); + LogUtils.DEBUG("isPkgProvider is: " + isPkgProvider); + + // TODO should all be done in this class instead of + // delegation???? + m_xBrowseNodeProxy = + new ProviderBrowseNode(this, m_container, m_xContext); + + m_xInvocationProxy = + UnoRuntime.queryInterface(XInvocation.class, m_xBrowseNodeProxy); + m_xPropertySetProxy = + UnoRuntime.queryInterface(XPropertySet.class, m_xBrowseNodeProxy); + } else { + // this is ok, for example when executing a script from the + // command line + LogUtils.DEBUG("ScriptProviderFor" + language + + " initialized without a context"); + } + + LogUtils.DEBUG("leaving XInit"); + } + + /** + * Gets the types attribute of the ScriptProvider object + * + * @return The types value + */ + public com.sun.star.uno.Type[] getTypes() { + Type[] retValue = new Type[ 8 ]; + retValue[ 0 ] = new Type(XScriptProvider.class); + retValue[ 1 ] = new Type(XBrowseNode.class); + retValue[ 2 ] = new Type(XInitialization.class); + retValue[ 3 ] = new Type(XTypeProvider.class); + retValue[ 4 ] = new Type(XServiceInfo.class); + retValue[ 5 ] = new Type(XPropertySet.class); + retValue[ 6 ] = new Type(XInvocation.class); + retValue[ 7 ] = new Type(com.sun.star.container.XNameContainer.class); + + return retValue; + } + + /** + * Gets the implementationId attribute of the ScriptProvider object + * + * @return The implementationId value + */ + public byte[] getImplementationId() { + return new byte[0]; + } + + /** + * Gets the implementationName attribute of the ScriptProvider object + * + * @return The implementationName value + */ + public String getImplementationName() { + return getClass().getName(); + } + + /** + * Description of the Method + * + * @param serviceName Description of the Parameter + * @return Description of the Return Value + */ + public boolean supportsService(String serviceName) { + for (int index = __serviceNames.length; index-- > 0;) { + if (serviceName.equals(__serviceNames[ index ])) { + return true; + } + } + + return false; + } + + /** + * Gets the supportedServiceNames attribute of the ScriptProvider object + * + * @return The supportedServiceNames value + */ + public String[] getSupportedServiceNames() { + return __serviceNames; + } + + public abstract XScript getScript(/*IN*/String scriptURI) throws + com.sun.star.uno.RuntimeException, ScriptFrameworkErrorException; + + // TODO need to encapsulate this better, + // Some factory concept for creating/accessing Editor + // How this is passed down or how it is accessible by BrowseNode + // implementations needs thinking about + // This method is used to determine whether the ScriptProvider + // has a ScriptEditor + public abstract boolean hasScriptEditor(); + + // TODO see above + // This method is used to get the ScriptEditor for this ScriptProvider + public abstract ScriptEditor getScriptEditor(); + + public ScriptMetaData getScriptData(/*IN*/String scriptURI) throws + ScriptFrameworkErrorException { + + try { + ParsedScriptUri details = m_container.parseScriptUri(scriptURI); + + try { + ScriptMetaData scriptData = m_container.findScript(details); + + if (scriptData == null) { + throw new ScriptFrameworkErrorException( + details.function + " does not exist", null, details.function, language, + ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + } + + return scriptData; + } catch (com.sun.star.container.NoSuchElementException nse) { + ScriptFrameworkErrorException e2 = + new ScriptFrameworkErrorException(nse.getMessage(), null, details.function, + language, ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + e2.initCause(nse); + throw e2; + } catch (com.sun.star.lang.WrappedTargetException wta) { + // TODO specify the correct error Type + java.lang.Exception wrapped = (java.lang.Exception) wta.TargetException; + String message = wta.getMessage(); + + if (wrapped != null) { + message = wrapped.getMessage(); + } + + ScriptFrameworkErrorException e2 = + new ScriptFrameworkErrorException(message, null, details.function, language, + ScriptFrameworkErrorType.UNKNOWN); + e2.initCause(wta); + throw e2; + } + } catch (com.sun.star.lang.IllegalArgumentException ila) { + // TODO specify the correct error Type + ScriptFrameworkErrorException e2 + = new ScriptFrameworkErrorException( + ila.getMessage(), null, scriptURI, language, + ScriptFrameworkErrorType.UNKNOWN); + e2.initCause(ila); + throw e2; + } + } + + // Implementation of XBrowseNode interface + public String getName() { + return language; + } + + public XBrowseNode[] getChildNodes() { + if (m_xBrowseNodeProxy == null) { + LogUtils.DEBUG("No Nodes available "); + return new XBrowseNode[0]; + } + + return m_xBrowseNodeProxy .getChildNodes(); + } + + public boolean hasChildNodes() { + if (m_xBrowseNodeProxy == null) { + LogUtils.DEBUG("No Nodes available "); + return false; + } + + return m_xBrowseNodeProxy.hasChildNodes(); + } + + public short getType() { + return BrowseNodeTypes.CONTAINER; + } + + @Override + public String toString() { + return getName(); + } + + // implementation of XInvocation interface + public XIntrospectionAccess getIntrospection() { + return m_xInvocationProxy.getIntrospection(); + } + + public Object invoke(String aFunctionName, Object[] aParams, + short[][] aOutParamIndex, Object[][] aOutParam) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + + return m_xInvocationProxy.invoke( + aFunctionName, aParams, aOutParamIndex, aOutParam); + } + + public void setValue(String aPropertyName, Object aValue) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + + m_xInvocationProxy.setValue(aPropertyName, aValue); + } + + public Object getValue(String aPropertyName) throws + com.sun.star.beans.UnknownPropertyException { + return m_xInvocationProxy.getValue(aPropertyName); + } + + public boolean hasMethod(String aName) { + return m_xInvocationProxy.hasMethod(aName); + } + + public boolean hasProperty(String aName) { + return m_xInvocationProxy.hasProperty(aName); + } + + public XPropertySetInfo getPropertySetInfo() { + return m_xPropertySetProxy.getPropertySetInfo(); + } + + public void setPropertyValue(String aPropertyName, Object aValue) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.beans.PropertyVetoException, + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + m_xPropertySetProxy.setPropertyValue(aPropertyName, aValue); + } + + public Object getPropertyValue(String PropertyName) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.lang.WrappedTargetException { + + return m_xPropertySetProxy.getPropertyValue(PropertyName); + } + + public void addPropertyChangeListener(String aPropertyName, + XPropertyChangeListener xListener) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.lang.WrappedTargetException { + + m_xPropertySetProxy.addPropertyChangeListener(aPropertyName, xListener); + } + + public void removePropertyChangeListener( + String aPropertyName, XPropertyChangeListener aListener) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.lang.WrappedTargetException { + + m_xPropertySetProxy.removePropertyChangeListener( + aPropertyName, aListener); + } + + public void addVetoableChangeListener( + String PropertyName, XVetoableChangeListener aListener) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.lang.WrappedTargetException { + + m_xPropertySetProxy.addVetoableChangeListener(PropertyName, aListener); + } + + public void removeVetoableChangeListener(String PropertyName, + XVetoableChangeListener aListener) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.lang.WrappedTargetException { + + m_xPropertySetProxy.removeVetoableChangeListener( + PropertyName, aListener); + + } + public java.lang.Object getByName(String aName) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + // TODO needs implementing? + throw new com.sun.star.uno.RuntimeException("getByName not implemented"); + } + + public String[] getElementNames() { + // TODO needs implementing? + throw new com.sun.star.uno.RuntimeException("getElementNames not implemented"); + } + + // Performs the getRegStatus functionality for the PkgMgr + public boolean hasByName(String aName) { + return ((UnoPkgContainer)m_container).hasRegisteredUnoPkgContainer(aName); + } + + public com.sun.star.uno.Type getElementType() { + // TODO at the moment this returns void indicating + // type is unknown should indicate XPackage ? do we implement XPackage + return new Type(); + } + + public boolean hasElements() { + // TODO needs implementing? + throw new com.sun.star.uno.RuntimeException("hasElements not implemented"); + } + public void replaceByName(String aName, java.lang.Object aElement) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + // TODO needs implementing + throw new com.sun.star.uno.RuntimeException("replaceByName not implemented"); + } + + public void insertByName(String aName, java.lang.Object aElement) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.container.ElementExistException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("Provider for " + language + " received register for package " + + aName); + + XPackage newPackage = UnoRuntime.queryInterface(XPackage.class, aElement); + + if (aName == null || aName.length() == 0) { + throw new com.sun.star.lang.IllegalArgumentException("Empty name"); + } + + if (newPackage == null) { + throw new com.sun.star.lang.IllegalArgumentException("No package supplied"); + } + + ((UnoPkgContainer)m_container).processUnoPackage(newPackage, language); + } + + // de-register for library only !! + public void removeByName(String Name) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("In ScriptProvider.removeByName() for " + Name + + " this provider = " + language); + + ParcelContainer c = + ((UnoPkgContainer)m_container).getRegisteredUnoPkgContainer( + Name); + + if (c != null) { + String libName; + + if (Name.endsWith("/")) { + String tmp = Name.substring(0, Name.lastIndexOf('/')); + libName = tmp.substring(tmp.lastIndexOf('/') + 1); + } else { + libName = Name.substring(Name.lastIndexOf('/') + 1); + } + + LogUtils.DEBUG("Deregistering library " + libName); + + if (c.removeParcel(libName)) { + ((UnoPkgContainer)m_container).deRegisterPackageContainer(Name); + } else { + throw new com.sun.star.container.NoSuchElementException( + libName + " cannot be removed from container."); + } + } else { + throw new com.sun.star.container.NoSuchElementException( + Name + " doesn't exist for " + language); + } + + // TODO see if we want to remove the ParcelContainer is no Parcels/Libraries left + } + + private String getDocUrlFromModel(XModel document) { + XTransientDocumentsDocumentContentFactory factory = null; + + try { + + factory = UnoRuntime.queryInterface( + XTransientDocumentsDocumentContentFactory.class, + m_xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.frame.TransientDocumentsDocumentContentFactory", + m_xContext)); + + } catch (Exception ex) { + } + + if (factory == null) + throw new com.sun.star.uno.RuntimeException( + "ScriptProvider: unable to create a TDOC context factory.", this); + + try { + XContent content = factory.createDocumentContent(document); + return content.getIdentifier().getContentIdentifier(); + } catch (com.sun.star.lang.IllegalArgumentException ex) { + } + + LogUtils.DEBUG("unable to determine the model's TDOC URL"); + return ""; + } + + private XModel getModelFromDocUrl(String docUrl) { + + LogUtils.DEBUG("getModelFromDocUrl - searching for match for ->" + + docUrl + "<-"); + + XModel xModel = null; + + try { + XUniversalContentBroker ucb = UniversalContentBroker.create(m_xContext); + + XContentIdentifier xCntId = ucb.createContentIdentifier(docUrl); + + XContent xCnt = ucb.queryContent(xCntId); + + XCommandProcessor xCmd = + UnoRuntime.queryInterface(XCommandProcessor.class, xCnt); + + Property[] pArgs = new Property[ ] { new Property() }; + pArgs[ 0 ].Name = "DocumentModel"; + pArgs[ 0 ].Handle = -1; + + Command command = new Command(); + + command.Handle = -1; + command.Name = "getPropertyValues"; + command.Argument = pArgs; + + com.sun.star.ucb.XCommandEnvironment env = null ; + Object result = xCmd.execute(command, 0, env) ; + + XRow values = UnoRuntime.queryInterface(XRow.class, result); + + xModel = UnoRuntime.queryInterface(XModel.class, + values.getObject(1, null)); + + } catch (Exception ignore) { + LogUtils.DEBUG("Failed to get model exception " + ignore); + } + + return xModel; + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/SwingInvocation.java b/scripting/java/com/sun/star/script/framework/provider/SwingInvocation.java new file mode 100644 index 000000000..af563fa8e --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/SwingInvocation.java @@ -0,0 +1,39 @@ +/* + * 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.script.framework.provider; + +import javax.swing.SwingUtilities; + +// On macOS, AWT/Swing must not be accessed from the AppKit thread, so call +// SwingUtilities.invokeLater always on a fresh thread to avoid that problem +// (also, the current thread must not wait for that fresh thread to terminate, +// as that would cause a deadlock if this thread is the AppKit thread): +public final class SwingInvocation { + + public static void invoke(final Runnable doRun) { + new Thread("SwingInvocation") { + @Override + public void run() { + SwingUtilities.invokeLater(doRun); + } + } .start(); + } + + private SwingInvocation() {} +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/MANIFEST.MF b/scripting/java/com/sun/star/script/framework/provider/beanshell/MANIFEST.MF new file mode 100644 index 000000000..689b6fde9 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/MANIFEST.MF @@ -0,0 +1,2 @@ +RegistrationClassName: com.sun.star.script.framework.provider.beanshell.ScriptProviderForBeanShell +UNO-Type-Path: diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/PlainSourceView.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/PlainSourceView.java new file mode 100644 index 000000000..e519587ff --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/PlainSourceView.java @@ -0,0 +1,395 @@ +/* + * 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.script.framework.provider.beanshell; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.KeyStroke; +import javax.swing.UIManager; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; +import javax.swing.text.BadLocationException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoManager; +import java.util.List; +import java.util.ArrayList; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PlainSourceView extends JScrollPane implements + ScriptSourceView, DocumentListener { + + private final ScriptSourceModel model; + private JTextArea ta; + private GlyphGutter gg; + private int linecount; + private boolean isModified = false; + private static final String undoKey = "Undo"; + private static final String redoKey = "Redo"; + private CompoundEdit compoundEdit = null; + private static final int noLimit = -1; + UndoManager undoManager; + private List<UnsavedChangesListener> unsavedListener = new ArrayList<UnsavedChangesListener>(); + + private static final Pattern tabPattern = Pattern.compile("^ *(\\t)"); + private static final Pattern indentationPattern = Pattern.compile("^([^\\S\\r\\n]*)(([^\\{])*\\{\\s*)*"); + + public PlainSourceView(ScriptSourceModel model) { + this.model = model; + initUI(); + model.setView(this); + } + + public void undo(){ + if(compoundEdit!=null){ + compoundEdit.end(); + undoManager.addEdit(compoundEdit); + compoundEdit = null; + } + if(undoManager.canUndo()){ + undoManager.undo(); + } + // check if it's the last undoable change + if(undoManager.canUndo() == false){ + setModified(false); + } + } + public void redo(){ + if(undoManager.canRedo()){ + undoManager.redo(); + } + } + public void clear() { + ta.setText(""); + } + + public void update() { + /* Remove ourselves as a DocumentListener while loading the source + so we don't get a storm of DocumentEvents during loading */ + ta.getDocument().removeDocumentListener(this); + + if (!isModified) { + int pos = ta.getCaretPosition(); + ta.setText(model.getText()); + + try { + ta.setCaretPosition(pos); + } catch (IllegalArgumentException iae) { + // do nothing and allow JTextArea to set its own position + } + } + + // scroll to currentPosition of the model + try { + int line = ta.getLineStartOffset(model.getCurrentPosition()); + Rectangle rect = ta.modelToView(line); + if (rect != null) { + ta.scrollRectToVisible(rect); + } + } catch (BadLocationException e) { + // couldn't scroll to line, do nothing + } + + gg.repaint(); + + // Add back the listener + ta.getDocument().addDocumentListener(this); + } + + public boolean isModified() { + return isModified; + } + + private void notifyListeners (boolean isUnsaved) { + for (UnsavedChangesListener listener : unsavedListener) { + listener.onUnsavedChanges(isUnsaved); + } + } + + public void setModified(boolean value) { + if(value != isModified) { + notifyListeners(value); + isModified = value; + } + } + + private void initUI() { + try{ + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch(Exception e){ + // What to do here + } + ta = new JTextArea(); + ta.setTabSize(4); + ta.setRows(15); + ta.setColumns(40); + ta.setLineWrap(false); + ta.insert(model.getText(), 0); + ta.setFont(new Font("Monospaced", ta.getFont().getStyle(), ta.getFont().getSize())); + undoManager = new UndoManager(); + undoManager.setLimit(noLimit); + ta.getDocument().addUndoableEditListener(new UndoableEditListener(){ + @Override + public void undoableEditHappened(UndoableEditEvent editEvent) { + if(compoundEdit == null){ + compoundEdit = new CompoundEdit(); + } + compoundEdit.addEdit(editEvent.getEdit()); + } + }); + + ta.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK), undoKey); + ta.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK), redoKey); + + ta.addKeyListener(new KeyAdapter(){ + @Override + public void keyPressed(KeyEvent ke) { + // if shift + tab was pressed, remove the first tab before any code begins + if (ke.isShiftDown() && ke.getKeyCode() == KeyEvent.VK_TAB) { + try { + int caretOffset = ta.getCaretPosition(); + int lineOffset = ta.getLineOfOffset(caretOffset); + int startOffset = ta.getLineStartOffset(lineOffset); + int endOffset = ta.getLineEndOffset(lineOffset); + + Matcher matcher = tabPattern.matcher(ta.getText(startOffset, endOffset - startOffset)); + if (matcher.find()) { + ta.replaceRange(null, startOffset + matcher.start(1), startOffset + matcher.end(1)); + } + } catch (BadLocationException e) { + // could not find correct location of the tab + } + } + // if the enter key was pressed, adjust indentation of the current line accordingly + if (ke.getKeyCode() == KeyEvent.VK_ENTER) { + try { + int caretOffset = ta.getCaretPosition(); + int lineOffset = ta.getLineOfOffset(caretOffset); + int startOffset = ta.getLineStartOffset(lineOffset); + int endOffset = ta.getLineEndOffset(lineOffset); + + Matcher matcher = indentationPattern.matcher(ta.getText(startOffset, endOffset - startOffset)); + // insert new line including indentation of the previous line + ta.insert("\n", caretOffset++); + if (matcher.find()) { + if (matcher.group(1).length() > 0) { + ta.insert(matcher.group(1), caretOffset++); + } + // if there is an open curly bracket in the current line, increase indentation level + if (matcher.group(3) != null) { + ta.insert("\t", caretOffset); + } + } + ke.consume(); + } catch (BadLocationException e) { + // could not find correct location of the indentation + } + } + } + + @Override + public void keyReleased(KeyEvent ke){ + if(ke.getKeyCode() == KeyEvent.VK_SPACE || ke.getKeyCode() == KeyEvent.VK_ENTER){ + compoundEdit.end(); + undoManager.addEdit(compoundEdit); + compoundEdit = null; + } + } + }); + + ta.getActionMap().put(undoKey, new AbstractAction(undoKey){ + @Override + public void actionPerformed(ActionEvent event) { + undo(); + } + }); + + ta.getActionMap().put(redoKey, new AbstractAction(redoKey){ + @Override + public void actionPerformed(ActionEvent event) { + redo(); + } + }); + + linecount = ta.getLineCount(); + + gg = new GlyphGutter(this); + + setViewportView(ta); + setRowHeaderView(gg); + + ta.getDocument().addDocumentListener(this); + } + + /* Implementation of DocumentListener interface */ + public void insertUpdate(DocumentEvent e) { + doChanged(); + } + + public void removeUpdate(DocumentEvent e) { + doChanged(); + } + + public void changedUpdate(DocumentEvent e) { + doChanged(); + } + + /* If the number of lines in the JTextArea has changed then update the + GlyphGutter */ + private void doChanged() { + setModified(true); + + if (linecount != ta.getLineCount()) { + gg.update(); + linecount = ta.getLineCount(); + } + } + + public String getText() { + return ta.getText(); + } + + public JTextArea getTextArea() { + return ta; + } + + public int getCurrentPosition() { + return model.getCurrentPosition(); + } + + public void addListener(UnsavedChangesListener toAdd) { + unsavedListener.add(toAdd); + } +} + +class GlyphGutter extends JComponent { + + private final PlainSourceView view; + private static final String DUMMY_STRING = "99"; + + GlyphGutter(PlainSourceView view) { + this.view = view; + update(); + } + + public void update() { + JTextArea textArea = view.getTextArea(); + Font font = textArea.getFont(); + setFont(font); + + FontMetrics metrics = getFontMetrics(font); + int h = metrics.getHeight(); + int lineCount = textArea.getLineCount() + 1; + + String dummy = Integer.toString(lineCount); + + if (dummy.length() < 2) { + dummy = DUMMY_STRING; + } + + Dimension d = new Dimension(); + d.width = metrics.stringWidth(dummy) + 16; + d.height = lineCount * h + 100; + setPreferredSize(d); + setSize(d); + } + + @Override + public void paintComponent(Graphics g) { + JTextArea textArea = view.getTextArea(); + + Font font = textArea.getFont(); + g.setFont(font); + + FontMetrics metrics = getFontMetrics(font); + Rectangle clip = g.getClipBounds(); + + g.setColor(getBackground()); + g.fillRect(clip.x, clip.y, clip.width, clip.height); + + int ascent = metrics.getMaxAscent(); + int h = metrics.getHeight(); + int lineCount = textArea.getLineCount() + 1; + + int startLine = clip.y / h; + int endLine = (clip.y + clip.height) / h + 1; + int width = getWidth(); + + if (endLine > lineCount) { + endLine = lineCount; + } + + for (int i = startLine; i < endLine; i++) { + String text; + text = Integer.toString(i + 1) + " "; + int y = i * h; + g.setColor(Color.blue); + g.drawString(text, 0, y + ascent); + int x = width - ascent; + + // if currentPosition is not -1 then a red arrow will be drawn + if (i == view.getCurrentPosition()) { + drawArrow(g, ascent, x, y); + } + } + } + + private void drawArrow(Graphics g, int ascent, int x, int y) { + Polygon arrow = new Polygon(); + int dx = x; + y += ascent - 10; + int dy = y; + arrow.addPoint(dx, dy + 3); + arrow.addPoint(dx + 5, dy + 3); + + for (x = dx + 5; x <= dx + 10; x++, y++) { + arrow.addPoint(x, y); + } + + for (x = dx + 9; x >= dx + 5; x--, y++) { + arrow.addPoint(x, y); + } + + arrow.addPoint(dx + 5, dy + 7); + arrow.addPoint(dx, dy + 7); + + g.setColor(Color.red); + g.fillPolygon(arrow); + g.setColor(Color.black); + g.drawPolygon(arrow); + } +} + diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptEditorForBeanShell.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptEditorForBeanShell.java new file mode 100644 index 000000000..31c26dc51 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptEditorForBeanShell.java @@ -0,0 +1,400 @@ +/* + * 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.script.framework.provider.beanshell; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.provider.ClassLoaderFactory; +import com.sun.star.script.framework.provider.ScriptEditor; +import com.sun.star.script.framework.provider.SwingInvocation; +import com.sun.star.script.provider.XScriptContext; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.Dimension; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.URL; + +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import javax.swing.JToolBar; +import javax.swing.BorderFactory; + +public class ScriptEditorForBeanShell extends ScriptEditor implements ActionListener { + + private JFrame frame; + private String filename; + + private ScriptSourceModel model; + private ScriptSourceView view; + + private URL scriptURL = null; + private ClassLoader cl = null; + private JButton saveBtn; + + // global ScriptEditorForBeanShell returned for getEditor() calls + private static ScriptEditorForBeanShell theScriptEditorForBeanShell; + + // global list of ScriptEditors, key is [external form of URL] of file being edited + private static Map<String, ScriptEditorForBeanShell> BEING_EDITED = + new HashMap<String, ScriptEditorForBeanShell>(); + + // template for new BeanShell scripts + private static String BSHTEMPLATE; + + // try to load the template for BeanShell scripts + static { + BSHTEMPLATE = "// BeanShell script"; + try { + URL url = ScriptEditorForBeanShell.class.getResource("template.bsh"); + if (url != null) { + InputStream in = url.openStream(); + StringBuilder buf = new StringBuilder(); + byte[] b = new byte[1024]; + int len; + + while ((len = in.read(b)) != -1) { + buf.append(new String(b, 0, len)); + } + + in.close(); + + BSHTEMPLATE = buf.toString(); + } + } catch (IOException ioe) { + } catch (Exception e) { + } + } + + /** + * Returns the global ScriptEditorForBeanShell instance. + */ + + public static synchronized ScriptEditorForBeanShell getEditor() { + if (theScriptEditorForBeanShell == null) { + theScriptEditorForBeanShell = + new ScriptEditorForBeanShell(); + } + + return theScriptEditorForBeanShell; + } + + /** + * Get the ScriptEditorForBeanShell instance for this URL + * + * @param url The URL of the script source file + * + * @return The ScriptEditorForBeanShell associated with + * the given URL if one exists, otherwise null. + */ + public static ScriptEditorForBeanShell getEditor(URL url) { + synchronized (BEING_EDITED) { + return BEING_EDITED.get(url.toExternalForm()); + } + } + + /** + * Returns whether or not the script source being edited in this + * ScriptEditorForBeanShell has been modified + */ + public boolean isModified() { + return view.isModified(); + } + + /** + * Returns the text being displayed in this ScriptEditorForBeanShell + * + * @return The text displayed in this ScriptEditorForBeanShell + */ + public String getText() { + return view.getText(); + } + + /** + * Returns the template text for BeanShell scripts + * + * @return The template text for BeanShell scripts + */ + public String getTemplate() { + return BSHTEMPLATE; + } + + /** + * Returns the default extension for BeanShell scripts + * + * @return The default extension for BeanShell scripts + */ + public String getExtension() { + return "bsh"; + } + + /** + * Indicates the line where error occurred + * + */ + public void indicateErrorLine(int lineNum) { + model.indicateErrorLine(lineNum); + } + + /** + * Executes the script edited by the editor + * + */ + public Object execute() throws Exception { + if (!isMacroExectionEnabled()) { + showErrorMessage("Macro Execution has been disabled."); + return null; + } + + frame.toFront(); + return model.execute(context, cl); + } + + /** + * Opens an editor window for the specified ScriptMetaData. + * If an editor window is already open for that data it will be + * moved to the front. + * + * @param context The context in which to execute the script + * @param entry The metadata describing the script + */ + public void edit(final XScriptContext context, ScriptMetaData entry) { + if (entry != null) { + try { + ClassLoader cl = null; + + try { + cl = ClassLoaderFactory.getURLClassLoader(entry); + } catch (Exception ignore) { // TODO re-examine error handling + } + + final ClassLoader theCl = cl; + final URL url = entry.getSourceURL(); + SwingInvocation.invoke( + new Runnable() { + public void run() { + ScriptEditorForBeanShell editor; + + synchronized (BEING_EDITED) { + editor = BEING_EDITED.get(url.toExternalForm()); + + if (editor == null) { + editor = new ScriptEditorForBeanShell( + context, theCl, url); + BEING_EDITED.put(url.toExternalForm(), editor); + } + } + + editor.frame.toFront(); + } + }); + } catch (IOException ioe) { + showErrorMessage("Error loading file: " + ioe.getMessage()); + } + } + } + + private ScriptEditorForBeanShell() { + } + + private ScriptEditorForBeanShell(XScriptContext context, ClassLoader cl, + URL url) { + setContext(context); + this.scriptURL = url; + this.model = new ScriptSourceModel(url); + this.filename = ScriptMetaData.getFileName(url); + this.cl = cl; + + try { + + Class<?> c = + Class.forName("org.openoffice.netbeans.editor.NetBeansSourceView"); + + Class<?>[] types = new Class[] { ScriptSourceModel.class }; + + java.lang.reflect.Constructor<?> ctor = c.getConstructor(types); + + if (ctor != null) { + Object[] args = new Object[] { this.model }; + this.view = (ScriptSourceView) ctor.newInstance(args); + } else { + this.view = new PlainSourceView(model); + } + } catch (java.lang.Error err) { + this.view = new PlainSourceView(model); + } catch (Exception e) { + this.view = new PlainSourceView(model); + } + + this.model.setView(this.view); + initUI(); + this.view.addListener(new UnsavedChangesListener() { + @Override + public void onUnsavedChanges(boolean isUnsaved) { + if(filename != null) { + // enable or disable save button depending on unsaved changes + saveBtn.setEnabled(isUnsaved); + } + } + }); + frame.setVisible(true); + } + + private void initUI() { + frame = new JFrame("BeanShell Debug Window: " + filename); + frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + frame.addWindowListener( + new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + doClose(); + } + } + ); + + String[] labels = {"Run", "Clear", "Save","Undo","Redo"}; + JToolBar toolbar = new JToolBar(); + toolbar.setRollover(true); + for (String label : labels) { + JButton b = new JButton(label); + b.setToolTipText(label); + b.addActionListener(this); + toolbar.add(b); + toolbar.addSeparator(); + + // disable save button on start + if (label.equals("Save")) { + b.setEnabled(false); + saveBtn = b; + } + } + + frame.getContentPane().add((JComponent)view, BorderLayout.CENTER); + frame.add(toolbar, BorderLayout.NORTH); + frame.pack(); + frame.setSize(590, 480); + frame.setLocation(300, 200); + frame.setMinimumSize(new Dimension(500, 300)); + } + + private void doClose() { + if (view.isModified()) { + + int result = JOptionPane.showConfirmDialog(frame, + "The script has been modified. Do you want to save the changes?"); + + if (result == JOptionPane.CANCEL_OPTION) { + // don't close the window, just return + return; + } else if (result == JOptionPane.YES_OPTION) { + boolean saveSuccess = saveTextArea(); + + if (!saveSuccess) { + return; + } + } + } + + frame.dispose(); + shutdown(); + } + + private boolean saveTextArea() { + boolean result = true; + + if (!view.isModified()) { + return true; + } + + OutputStream fos = null; + + try { + String s = view.getText(); + fos = scriptURL.openConnection().getOutputStream(); + + if (fos != null) { + fos.write(s.getBytes()); + } else { + showErrorMessage( + "Error saving script: Could not open stream for file"); + result = false; + } + + view.setModified(false); + } catch (IOException ioe) { + showErrorMessage("Error saving script: " + ioe.getMessage()); + result = false; + } catch (Exception e) { + showErrorMessage("Error saving script: " + e.getMessage()); + result = false; + } finally { + if (fos != null) { + try { + fos.flush(); + } catch (IOException ignore) { + } + try { + fos.close(); + } catch (IOException ignore) { + } + } + } + + return result; + } + + private void shutdown() { + synchronized (BEING_EDITED) { + BEING_EDITED.remove(scriptURL.toExternalForm()); + } + } + + public void actionPerformed(ActionEvent e) { + String actionCommand = e.getActionCommand(); + if (actionCommand.equals("Run")) { + try { + execute(); + } catch (Exception invokeException) { + showErrorMessage(invokeException.toString()); + } + } else if (actionCommand.equals("Save")) { + saveTextArea(); + } else if (actionCommand.equals("Clear")) { + view.clear(); + } else if(actionCommand.equals("Undo")){ + view.undo(); + } else if(actionCommand.equals("Redo")){ + view.redo(); + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptProviderForBeanShell.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptProviderForBeanShell.java new file mode 100644 index 000000000..2aabba712 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptProviderForBeanShell.java @@ -0,0 +1,367 @@ +/* + * 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.script.framework.provider.beanshell; + +import bsh.Interpreter; + +import com.sun.star.comp.loader.FactoryHelper; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XModel; + +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.reflection.InvocationTargetException; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ClassLoaderFactory; +import com.sun.star.script.framework.provider.ScriptContext; +import com.sun.star.script.framework.provider.ScriptEditor; +import com.sun.star.script.framework.provider.ScriptProvider; +import com.sun.star.script.provider.ScriptErrorRaisedException; +import com.sun.star.script.provider.ScriptExceptionRaisedException; +import com.sun.star.script.provider.ScriptFrameworkErrorException; +import com.sun.star.script.provider.ScriptFrameworkErrorType; +import com.sun.star.script.provider.XScript; + +import com.sun.star.uno.Any; +import com.sun.star.uno.Type; +import com.sun.star.uno.XComponentContext; + +import java.net.URL; + +import java.util.StringTokenizer; + +public class ScriptProviderForBeanShell { + + public static class ScriptProviderForBeanShell_2 extends ScriptProvider { + + public ScriptProviderForBeanShell_2(XComponentContext ctx) { + super(ctx, "BeanShell"); + } + + @Override + public XScript getScript(/*IN*/String scriptURI) throws + com.sun.star.uno.RuntimeException, ScriptFrameworkErrorException { + + ScriptMetaData scriptData = getScriptData(scriptURI); + + try { + + ScriptImpl script = + new ScriptImpl(m_xContext, scriptData, m_xModel, m_xInvocContext); + + return script; + } catch (com.sun.star.uno.RuntimeException re) { + throw new ScriptFrameworkErrorException( + "Failed to create script object: " + re.getMessage(), + null, scriptData.getLanguageName(), language, + ScriptFrameworkErrorType.UNKNOWN); + } + } + + @Override + public boolean hasScriptEditor() { + return true; + } + + @Override + public ScriptEditor getScriptEditor() { + return ScriptEditorForBeanShell.getEditor(); + } + } + + /** + * Returns a factory for creating the service. + * 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 used 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( + ScriptProviderForBeanShell.ScriptProviderForBeanShell_2.class.getName())) { + + xSingleServiceFactory = + FactoryHelper.getServiceFactory( + ScriptProviderForBeanShell.ScriptProviderForBeanShell_2.class, + "com.sun.star.script.provider.ScriptProviderForBeanShell", + multiFactory, regKey); + + } + + return xSingleServiceFactory; + } +} + +class ScriptImpl implements XScript { + + private final ScriptMetaData metaData; + private final XComponentContext m_xContext; + private XMultiComponentFactory m_xMultiComponentFactory; + private final XModel m_xModel; + private final XScriptInvocationContext m_xInvocContext; + + ScriptImpl(XComponentContext ctx, ScriptMetaData metaData, XModel xModel, + XScriptInvocationContext xContext) throws + com.sun.star.uno.RuntimeException { + + this.metaData = metaData; + this.m_xContext = ctx; + this.m_xModel = xModel; + this.m_xInvocContext = xContext; + + try { + this.m_xMultiComponentFactory = m_xContext.getServiceManager(); + } catch (Exception e) { + throw new com.sun.star.uno.RuntimeException(e); + } + + LogUtils.DEBUG("ScriptImpl [beanshell] script data = " + metaData); + } + + /** + * documentStorageID and document reference + * for use in script name resolving + * + * @param aParams All parameters; pure, out params are + * undefined in sequence, i.e., the value + * has to be ignored by the callee + * + * @param aOutParamIndex Out indices + * + * @param aOutParam Out parameters + * + * @return The value returned from the function + * being invoked + * + * @throws IllegalArgumentException If there is no matching script name + * + * @throws InvocationTargetException If the running script throws + * an exception this information + * is captured and rethrown as + * this exception type. + */ + + public Object invoke(/*IN*/Object[] aParams, + /*OUT*/short[][] aOutParamIndex, + /*OUT*/Object[][] aOutParam) throws + ScriptFrameworkErrorException, InvocationTargetException { + + // Initialise the out parameters - not used at the moment + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + ClassLoader cl = null; + URL sourceUrl = null; + + try { + cl = ClassLoaderFactory.getURLClassLoader(metaData); + sourceUrl = metaData.getSourceURL(); + } catch (java.net.MalformedURLException mfu) { + // Framework error + throw new ScriptFrameworkErrorException( + mfu.getMessage(), null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.MALFORMED_URL); + } + + // Set class loader to be used for class files + // and jar files + Thread.currentThread().setContextClassLoader(cl); + Interpreter interpreter = new Interpreter(); + + interpreter.getNameSpace().clear(); + // Set class loader to be used by interpreter + // to look for classes by source e.g. interpreter + // will use this classloader to search classpath + // for source file ( bla.java ) on import or reference + interpreter.setClassLoader(cl); + + try { + + interpreter.set("XSCRIPTCONTEXT", + ScriptContext.createContext(m_xModel, m_xInvocContext, + m_xContext, m_xMultiComponentFactory)); + + interpreter.set("ARGUMENTS", aParams); + } catch (bsh.EvalError e) { + // Framework error setting up context + throw new ScriptFrameworkErrorException( + e.getMessage(), null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.UNKNOWN); + } + + try { + Object result; + + ScriptEditorForBeanShell editor = + ScriptEditorForBeanShell.getEditor(sourceUrl); + + if (editor != null) { + result = editor.execute(); + + if (result == null) { + return new Any(new Type(), null); + } + + return result; + } + + metaData.loadSource(); + String source = metaData.getSource(); + + if (source == null || source.length() == 0) { + throw new ScriptFrameworkErrorException( + "Failed to read script", null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + } + + result = interpreter.eval(source); + + if (result == null) { + return new Any(new Type(), null); + } + + return result; + } catch (bsh.ParseException pe) { + throw new InvocationTargetException( + "Beanshell failed to parse " + metaData.getLanguageName(), + null, processBshException(pe, metaData.getLanguageName())); + } catch (bsh.TargetError te) { + throw new InvocationTargetException( + "Beanshell uncaught exception for " + metaData.getLanguageName(), + null, processBshException(te, metaData.getLanguageName())); + } catch (bsh.EvalError ex) { + throw new InvocationTargetException( + "Beanshell error for " + metaData.getLanguageName(), + null, processBshException(ex, metaData.getLanguageName())); + } catch (Exception e) { + throw new ScriptFrameworkErrorException( + "Failed to read script", null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.UNKNOWN); + } + } + private void raiseEditor(int lineNum) { + try { + URL sourceUrl = metaData.getSourceURL(); + + ScriptEditorForBeanShell editor = + ScriptEditorForBeanShell.getEditor(sourceUrl); + + if (editor == null) { + editor = ScriptEditorForBeanShell.getEditor(); + + editor.edit( + ScriptContext.createContext(m_xModel, m_xInvocContext, m_xContext, + m_xMultiComponentFactory), metaData); + + editor = ScriptEditorForBeanShell.getEditor(sourceUrl); + } + + if (editor != null) { + editor.indicateErrorLine(lineNum); + } + } catch (java.net.MalformedURLException ignore) { + } + } + + private ScriptErrorRaisedException processBshException( + bsh.EvalError e, String script) { + + LogUtils.DEBUG("Beanshell error RAW message " + e.getMessage()); + String message = e.getMessage(); + int usefulInfoIndex = message.lastIndexOf("\' :"); + int lineNum = e.getErrorLineNumber(); + + raiseEditor(lineNum); + + if (usefulInfoIndex > -1) { + message = message.substring(usefulInfoIndex + 2); + } + + if (e instanceof bsh.TargetError) { + LogUtils.DEBUG("got instance of TargetError"); + + if (usefulInfoIndex == -1) { + message = ((bsh.TargetError)e).getTarget().getMessage(); + } + + String wrappedException = ""; + String full = e.toString(); + int index = full.indexOf("Target exception:"); + + if (index > -1) { + String toParse = full.substring(index); + LogUtils.DEBUG("About to parse " + toParse); + + StringTokenizer tokenizer = + new StringTokenizer(full.substring(index), ":"); + + if (tokenizer.countTokens() > 2) { + LogUtils.DEBUG("First token = " + tokenizer.nextToken()); + wrappedException = tokenizer.nextToken(); + LogUtils.DEBUG("wrapped exception = = " + wrappedException); + } + } + + ScriptExceptionRaisedException se = + new ScriptExceptionRaisedException(message); + + se.lineNum = lineNum; + se.scriptName = script; + se.exceptionType = wrappedException; + se.language = "BeanShell"; + + LogUtils.DEBUG("UnCaught Exception error: "); + LogUtils.DEBUG("\tscript: " + script); + LogUtils.DEBUG("\tline: " + lineNum); + LogUtils.DEBUG("\twrapped exception: " + wrappedException); + LogUtils.DEBUG("\tmessage: " + message); + + return se; + + } else { + LogUtils.DEBUG("Error or ParseError Exception error: "); + LogUtils.DEBUG("\tscript: " + script); + LogUtils.DEBUG("\tline: " + lineNum); + LogUtils.DEBUG("\tmessage: " + message); + + return new ScriptErrorRaisedException(message, null, script, + "BeanShell", lineNum); + + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceModel.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceModel.java new file mode 100644 index 000000000..165f00844 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceModel.java @@ -0,0 +1,125 @@ +/* + * 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.script.framework.provider.beanshell; + +import com.sun.star.script.provider.XScriptContext; + +import java.io.IOException; +import java.io.InputStream; + +import java.net.URL; + +public class ScriptSourceModel { + + private int currentPosition = -1; + private final URL file; + private ScriptSourceView view = null; + + public ScriptSourceModel(URL file) { + this.file = file; + } + + private String load() throws IOException { + StringBuilder buf = new StringBuilder(); + InputStream in = file.openStream(); + + byte[] contents = new byte[1024]; + int len; + + while ((len = in.read(contents, 0, 1024)) != -1) { + buf.append(new String(contents, 0, len)); + } + + try { + in.close(); + } catch (IOException ignore) { + } + + return buf.toString(); + } + + public String getText() { + String result = ""; + + try { + result = load(); + } catch (IOException ioe) { + // do nothing, empty string will be returned + } + + return result; + } + + public int getCurrentPosition() { + return this.currentPosition; + } + + public void setView(ScriptSourceView view) { + this.view = view; + } + + public Object execute(final XScriptContext context, ClassLoader cl) + throws Exception { + if (cl != null) { + // sets this threads class loader + // hopefully any threads spawned by this + // will inherit this cl + // this enables any class files imported + // from the interpreter to be loaded + // note: setting the classloader on the + // interpreter has a slightly different + // meaning in that the classloader for + // the interpreter seems only to look for + // source files ( bla.java ) in the classpath + Thread.currentThread().setContextClassLoader(cl); + } + + bsh.Interpreter interpreter = new bsh.Interpreter(); + + if (cl != null) { + // additionally set class loader on the interpreter + // to allow it to load java classes defined in source + // files e.g. bla.java + interpreter.getNameSpace().clear(); + } + + + // reset position + currentPosition = -1; + view.update(); + + interpreter.set("XSCRIPTCONTEXT", context); + interpreter.set("ARGUMENTS", new Object[0]); + + Object result; + + if (view.isModified()) { + result = interpreter.eval(view.getText()); + } else { + result = interpreter.eval(getText()); + } + + return result; + } + + public void indicateErrorLine(int lineNum) { + System.out.println("Beanshell indicateErrorLine " + lineNum); + currentPosition = lineNum - 1; + view.update(); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceView.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceView.java new file mode 100644 index 000000000..e39511c24 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceView.java @@ -0,0 +1,29 @@ +/* + * 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.script.framework.provider.beanshell; + +public interface ScriptSourceView { + void clear(); + void update(); + boolean isModified(); + void setModified(boolean value); + String getText(); + void undo(); + void redo(); + void addListener(UnsavedChangesListener toAdd); +} diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/UnsavedChangesListener.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/UnsavedChangesListener.java new file mode 100644 index 000000000..8efb2087c --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/UnsavedChangesListener.java @@ -0,0 +1,13 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ +package com.sun.star.script.framework.provider.beanshell; + +public interface UnsavedChangesListener { + void onUnsavedChanges(boolean isModified); +} diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/template.bsh b/scripting/java/com/sun/star/script/framework/provider/beanshell/template.bsh new file mode 100644 index 000000000..3bf6d22a3 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/template.bsh @@ -0,0 +1,65 @@ +/* + * 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 . + */ +/* + Import standard OpenOffice.org API classes. For more information on + these classes and the OpenOffice.org API, see the OpenOffice.org + Developers Guide at: + + https://api.libreoffice.org/ +*/ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.frame.XDesktop; +import com.sun.star.frame.XModel; + +/* + Import XScriptContext class. An instance of this class is available + to all BeanShell scripts in the global variable "XSCRIPTCONTEXT". This + variable can be used to access the document for which this script + was invoked. + + Methods available are: + + XSCRIPTCONTEXT.getDocument() returns XModel + XSCRIPTCONTEXT.getInvocationContext() returns XScriptInvocationContext or NULL + XSCRIPTCONTEXT.getDesktop() returns XDesktop + XSCRIPTCONTEXT.getComponentContext() returns XComponentContext + + For more information on using this class see the scripting + developer guides at: + + https://api.libreoffice.org/docs/DevelopersGuide/ScriptingFramework/ScriptingFramework.xhtml +*/ + +// Hello World in BeanShell +import com.sun.star.text.XTextDocument; +import com.sun.star.text.XText; +import com.sun.star.text.XTextRange; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +xTextDoc = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class,oDoc); +xText = xTextDoc.getText(); +xTextRange = xText.getEnd(); +xTextRange.setString( "Hello World (in BeanShell)" ); + +// BeanShell scripts in LibreOffice should always return 0 +return 0; diff --git a/scripting/java/com/sun/star/script/framework/provider/java/MANIFEST.MF b/scripting/java/com/sun/star/script/framework/provider/java/MANIFEST.MF new file mode 100644 index 000000000..f2056d0c4 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/MANIFEST.MF @@ -0,0 +1,2 @@ +RegistrationClassName: com.sun.star.script.framework.provider.java.ScriptProviderForJava +UNO-Type-Path: diff --git a/scripting/java/com/sun/star/script/framework/provider/java/Resolver.java b/scripting/java/com/sun/star/script/framework/provider/java/Resolver.java new file mode 100644 index 000000000..8192f4724 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/Resolver.java @@ -0,0 +1,36 @@ +/* + * 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.script.framework.provider.java; + +/** + * The Resolver interface is an interface common to all classes which + * want to implement an algorithm for obtaining a ScriptProxy object + * for a particular ScriptDescriptor and Class + */ +public interface Resolver { + + /** + * Returns a ScriptProxy object for the given ScriptDescriptor and Class + * + * @param sd A script Descriptor + * @param c A Class + * @return The ScriptProxy value + */ + ScriptProxy getProxy(ScriptDescriptor sd, Class<?> c) throws + NoSuchMethodException; +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/java/ScriptDescriptor.java b/scripting/java/com/sun/star/script/framework/provider/java/ScriptDescriptor.java new file mode 100644 index 000000000..ff56564b2 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/ScriptDescriptor.java @@ -0,0 +1,165 @@ +/* + * 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.script.framework.provider.java; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +/** + * The <code>ScriptDescriptor</code> object is used to store the search + * criteria that should be used for finding a particular script + */ +public class ScriptDescriptor { + + private final String m_name; + private final String m_methodName; + private final String m_className; + private List<String> m_classpath; + private final ArrayList<Class<?>> m_argumentTypes = new ArrayList<Class<?>>(11); + + /** + * Constructs a ScriptDescriptor for the given name + * + * @param name Script Name + * @exception IllegalArgumentException if the given name does not contain a "." + */ + public ScriptDescriptor(String name) throws IllegalArgumentException { + int idx = name.lastIndexOf('.'); + + if (idx == -1) { + throw new IllegalArgumentException("Invalid method name"); + } + + this.m_name = name; + this.m_methodName = name.substring(idx + 1); + this.m_className = name.substring(0, idx); + } + + /** + * Gets the fully qualified name of this <code>ScriptDescriptor</code> + * + * @return The Script Name value + */ + public String getName() { + return m_name; + } + + /** + * Gets the fully qualified name of this <code>ScriptDescriptor</code> + * + * @return The Script Name value + */ + public String getClassName() { + return m_className; + } + + /** + * Gets the method name of this <code>ScriptDescriptor</code> + * + * @return The methodName value + */ + public String getMethodName() { + return m_methodName; + } + + /** + * Sets the classpath value stored by this <code>ScriptDescriptor</code> + * + * @param classpath The new classpath value + */ + public void setClasspath(String classpath) { + StringTokenizer stk = new StringTokenizer(classpath, ":"); + + while (stk.hasMoreTokens()) { + this.m_classpath.add(stk.nextToken()); + } + } + + /** + * Sets the classpath value stored by this <code>ScriptDescriptor</code> + * + * @param classpath The new classpath value + */ + public void setClasspath(List<String> classpath) { + this.m_classpath = classpath; + } + + /** + * Gets the classpath value stored by this <code>ScriptDescriptor</code> + * + * @return The classpath value + */ + public List<String> getClasspath() { + return m_classpath; + } + + /** + * Adds the given <code>Class</code> to the list of argument types stored in + * this ScriptDescriptor + * + * @param clazz The feature to be added to the ArgumentType attribute + */ + + public synchronized void addArgumentType(Class<?> clazz) { + m_argumentTypes.add(clazz); + } + + /** + * Gets a list of the types of the arguments stored in this + * <code>ScriptDescriptor</code> + * + * return the argument types as an array of Class + * + * @return The argumentTypes value + */ + + public synchronized Class<?>[] getArgumentTypes() { + if (m_argumentTypes.size() > 0) + return m_argumentTypes.toArray(new Class[ m_argumentTypes.size() ]); + else + return null; + } + + /** + * Returns a <code>String</code> representation of this + * <code>ScriptDescriptor</code> + * + * @return The scriptName including the parameters. + */ + @Override + public String toString() { + StringBuilder description = new StringBuilder(m_name); + Class<?>[] types = getArgumentTypes(); + + description.append(" ("); + + if (types != null) { + for (int i = 0; i < types.length - 1; i++) { + description.append(types[ i ].getName()); + description.append(", "); + } + + description.append(types[ types.length - 1 ].getName()); + } + + description.append(")"); + + return description.toString(); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/java/ScriptProviderForJava.java b/scripting/java/com/sun/star/script/framework/provider/java/ScriptProviderForJava.java new file mode 100644 index 000000000..63dc3169f --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/ScriptProviderForJava.java @@ -0,0 +1,316 @@ +/* + * 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.script.framework.provider.java; + +import com.sun.star.comp.loader.FactoryHelper; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XModel; + +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ClassLoaderFactory; +import com.sun.star.script.framework.provider.ScriptContext; +import com.sun.star.script.framework.provider.ScriptEditor; +import com.sun.star.script.framework.provider.ScriptProvider; +import com.sun.star.script.provider.ScriptExceptionRaisedException; +import com.sun.star.script.provider.ScriptFrameworkErrorException; +import com.sun.star.script.provider.ScriptFrameworkErrorType; +import com.sun.star.script.provider.XScript; +import com.sun.star.script.provider.XScriptContext; + +import com.sun.star.uno.Any; +import com.sun.star.uno.Type; +import com.sun.star.uno.XComponentContext; + +import java.io.IOException; +import java.net.URLClassLoader; +import java.util.ArrayList; + +public class ScriptProviderForJava { + + public static class _ScriptProviderForJava extends ScriptProvider { + + private final Resolver m_resolutionPolicy = new StrictResolver(); + + public _ScriptProviderForJava(XComponentContext ctx) { + super(ctx, "Java"); + } + + @Override + public XScript getScript(/*IN*/String scriptURI) throws + com.sun.star.uno.RuntimeException, ScriptFrameworkErrorException { + + ScriptMetaData scriptData = getScriptData(scriptURI); + + try { + + ScriptImpl script = + new ScriptImpl(m_xContext, m_resolutionPolicy, scriptData, m_xModel, + m_xInvocContext); + + return script; + } catch (com.sun.star.uno.RuntimeException re) { + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + "Failed to create script object: " + re, null, + scriptData.getLanguageName(), language, + ScriptFrameworkErrorType.UNKNOWN); + e2.initCause(re); + throw e2; + } + } + + @Override + public boolean hasScriptEditor() { + return false; + } + + @Override + public ScriptEditor getScriptEditor() { + return null; + } + } + + /** + * Returns a factory for creating the service. + * 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 used 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( + ScriptProviderForJava._ScriptProviderForJava.class.getName())) { + + xSingleServiceFactory = + FactoryHelper.getServiceFactory( + ScriptProviderForJava._ScriptProviderForJava.class, + "com.sun.star.script.provider.ScriptProviderForJava", + multiFactory, regKey); + + } + + return xSingleServiceFactory; + } +} + +class ScriptImpl implements XScript { + + private final ScriptMetaData metaData; + private final XComponentContext m_xContext; + private final XModel m_xModel; + private final XScriptInvocationContext m_xInvocContext; + private XMultiComponentFactory m_xMultiComponentFactory; + private final Resolver m_resolutionPolicy; + + ScriptImpl(XComponentContext ctx, Resolver resolver, ScriptMetaData metaData, + XModel xModel, XScriptInvocationContext xInvocContext) throws + com.sun.star.uno.RuntimeException { + + this.metaData = metaData; + this.m_xContext = ctx; + this.m_xModel = xModel; + this.m_xInvocContext = xInvocContext; + this.m_resolutionPolicy = resolver; + + try { + this.m_xMultiComponentFactory = m_xContext.getServiceManager(); + } catch (Exception e) { + LogUtils.DEBUG(LogUtils.getTrace(e)); + throw new com.sun.star.uno.RuntimeException( + e, "Error constructing ScriptProvider: " + e); + } + + LogUtils.DEBUG("ScriptImpl [java] script data = " + metaData); + } + + /** + * Invoke + * + * + * @param params All parameters; pure, out params are undefined in + * sequence, i.e., the value has to be ignored by the callee + * @param aOutParamIndex Out indices + * @param aOutParam Out parameters + * @return The value returned from the function being invoked + * @throws IllegalArgumentException If there is no matching script name + * @throws com.sun.star.reflection.InvocationTargetException If the running script throws an exception + * this information is captured and rethrown as this exception type. + */ + public Object invoke( + /*IN*/Object[] params, + /*OUT*/short[][] aOutParamIndex, + /*OUT*/Object[][] aOutParam) throws + ScriptFrameworkErrorException, + com.sun.star.reflection.InvocationTargetException { + + LogUtils.DEBUG("** ScriptProviderForJava::invoke: Starting..."); + + // Initialise the out parameters - not used at the moment + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + ScriptDescriptor scriptDesc = + new ScriptDescriptor(metaData.getLanguageName()); + + Object[] invocationArgs = null; + ScriptProxy script = null; + + LogUtils.DEBUG("Classloader starting..."); + + try (URLClassLoader scriptLoader = ClassLoaderFactory.getURLClassLoader(metaData)) { + LogUtils.DEBUG("Classloader finished..."); + + ArrayList<Object> invocationArgList = new ArrayList<Object>(); + + LogUtils.DEBUG("Parameter Mapping..."); + + // Setup Context Object + XScriptContext xSc = + ScriptContext.createContext(m_xModel, m_xInvocContext, + m_xContext, m_xMultiComponentFactory); + + scriptDesc.addArgumentType(XScriptContext.class); + invocationArgList.add(xSc); + + for (int i = 0; i < params.length; i++) { + scriptDesc.addArgumentType(params[ i ].getClass()); + invocationArgList.add(params[ i ]); + } + + if (!invocationArgList.isEmpty()) { + invocationArgs = invocationArgList.toArray(); + } + + LogUtils.DEBUG("ScriptProxy starting... "); + + try { + + String className = metaData.getLanguageName().substring(0, + metaData.getLanguageName().lastIndexOf('.')); + + LogUtils.DEBUG("About to load Class " + className + " starting... "); + + long start = new java.util.Date().getTime(); + Class<?> c = scriptLoader.loadClass(className); + long end = new java.util.Date().getTime(); + + LogUtils.DEBUG("loadClass took: " + (end - start) + "milliseconds"); + + try { + LogUtils.DEBUG("class loaded ... "); + script = m_resolutionPolicy.getProxy(scriptDesc, c); + LogUtils.DEBUG("script resolved ... "); + } catch (NoSuchMethodException e) { + // Framework error + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + e.toString(), null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + e2.initCause(e); + throw e2; + } + } catch (ClassNotFoundException e) { + // Framework error + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + e.toString(), null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + e2.initCause(e); + throw e2; + } + } catch (IOException e) { + // Framework error + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + e.toString(), null, metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + e2.initCause(e); + throw e2; + } + + LogUtils.DEBUG("Starting Invoke on Proxy ..."); + Object result = null; + + try { + long start = new java.util.Date().getTime(); + result = script.invoke(invocationArgs); + long end = new java.util.Date().getTime(); + LogUtils.DEBUG("invoke took: " + (end - start) + "milliseconds"); + } catch (java.lang.IllegalArgumentException iae) { + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + iae.getMessage(), null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.UNKNOWN); + e2.initCause(iae); + throw e2; + } catch (java.lang.IllegalAccessException ia) { + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + ia.toString(), null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.UNKNOWN); + e2.initCause(ia); + throw e2; + } catch (java.lang.reflect.InvocationTargetException ite) { + Throwable targetException = ite.getTargetException(); + + ScriptExceptionRaisedException se = + new ScriptExceptionRaisedException(targetException.toString()); + + se.lineNum = -1; + se.scriptName = metaData.getLanguageName(); + se.language = "Java"; + se.exceptionType = targetException.getClass().getName(); + + throw new com.sun.star.reflection.InvocationTargetException( + "Scripting Framework error executing script ", null, se); + + } catch (Exception unknown) { + ScriptExceptionRaisedException se = + new ScriptExceptionRaisedException(unknown.toString()); + se.lineNum = -1; + se.scriptName = metaData.getLanguageName(); + se.language = "Java"; + se.exceptionType = unknown.getClass().getName(); + throw new com.sun.star.reflection.InvocationTargetException( + "Scripting Framework error executing script ", null, se); + } + + if (result == null) { + LogUtils.DEBUG("Got Nothing Back"); + // in the case where there is no return type + Any voidAny = new Any(new Type(), null); + result = voidAny; + } else { + LogUtils.DEBUG("Got object " + result); + } + + return result; + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/java/ScriptProxy.java b/scripting/java/com/sun/star/script/framework/provider/java/ScriptProxy.java new file mode 100644 index 000000000..921b282cc --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/ScriptProxy.java @@ -0,0 +1,69 @@ +/* + * 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.script.framework.provider.java; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * A ScriptProxy object acts as a proxy for a Java <code>Method</code> + * + * @see java.lang.reflect.Method + */ +public class ScriptProxy { + + private Object m_targetObject; + private final Method m_method; + + /** + * Constructs a <code>ScriptProxy</code> object for the given + * <code>Method</code> + * + * @param method Description of the Parameter + */ + public ScriptProxy(Method method) { + this.m_method = method; + } + + /** + * Sets the <code>Object</code> on which the ScriptProxy should invoke + * the method + * + * @param obj The new targetObject value + */ + public void setTargetObject(Object obj) { + m_targetObject = obj; + } + + /** + * Invokes the method contained in this <code>ScriptProxy</code>, + * any exceptions resulting from the invocation will be thrown + * + * @param args the arguments to be passed when invoking + * the method + * @return the Object returned from the method + * invocation, may be null + * @see java.lang.reflect.Method for the exceptions + * that may be thrown + */ + public Object invoke(Object[] args) throws + IllegalAccessException, InvocationTargetException, IllegalArgumentException { + + return m_method.invoke(m_targetObject, args); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/java/StrictResolver.java b/scripting/java/com/sun/star/script/framework/provider/java/StrictResolver.java new file mode 100644 index 000000000..08d06dce3 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/StrictResolver.java @@ -0,0 +1,113 @@ +/* + * 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.script.framework.provider.java; + +import com.sun.star.script.framework.log.LogUtils; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * A StrictResolver can be used to get a ScriptProxy object for a given + * ScriptDescriptor and Class. The StrictResolver is an implementation of + * the Resolver strategy. It will only return a ScriptProxy object if a + * method accepting all of the arguments specified in the ScriptDescriptor + * can be found in the Class. + */ +public class StrictResolver implements Resolver { + + /** + *Constructor for the StrictResolver object + */ + public StrictResolver() { + LogUtils.DEBUG(this.getClass().getName() + " created"); + } + + /** + * Returns a ScriptProxy object for the given ScriptDescriptor and Class. + * Only a strict match will be returned ie. where all of the arguments in + * the given ScriptDescriptor match the types of the + * + * @param sd the ScriptDescriptor for which a ScriptProxy is required + * @param c the Class file in which to search for the method + * @return the ScriptProxy matching the criteria, or null if no match is found + */ + public ScriptProxy getProxy(ScriptDescriptor sd, Class<?> c) throws + NoSuchMethodException { + + Method m = null; + + LogUtils.DEBUG("StrictResolver.getProxy() for: " + sd.toString()); + + try { + m = resolveArguments(sd, c); + } catch (ClassNotFoundException ex1) { + NoSuchMethodException ex2 = new NoSuchMethodException( + "StrictResolver.getProxy: Can't find method: " + sd.getMethodName()); + ex2.initCause(ex1); + throw ex2; + } catch (NoSuchMethodException ex1) { + NoSuchMethodException ex2 = new NoSuchMethodException( + "StrictResolver.getProxy: Can't find method: " + sd.getMethodName()); + ex2.initCause(ex1); + throw ex2; + } + + ScriptProxy sp = new ScriptProxy(m); + + int modifiers = m.getModifiers(); + + if (!Modifier.isStatic(modifiers)) { + Object o; + + try { + o = c.newInstance(); + } catch (InstantiationException ex1) { + NoSuchMethodException ex2 = new NoSuchMethodException( + "getScriptProxy: Can't instantiate: " + c.getName()); + ex2.initCause(ex1); + throw ex2; + } catch (IllegalAccessException ex1) { + NoSuchMethodException ex2 = new NoSuchMethodException( + "getScriptProxy: Can't access: " + c.getName()); + ex2.initCause(ex1); + throw ex2; + } + + sp.setTargetObject(o); + } + + return sp; + } + + /** + * Description of the Method + * + * @param sd Description of the Parameter + * @param c Description of the Parameter + * @return Description of the Return Value + * @exception ClassNotFoundException + * @exception NoSuchMethodException + */ + private Method resolveArguments(ScriptDescriptor sd, Class<?> c) throws + ClassNotFoundException, NoSuchMethodException { + + return c.getMethod(sd.getMethodName(), sd.getArgumentTypes()); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/MANIFEST.MF b/scripting/java/com/sun/star/script/framework/provider/javascript/MANIFEST.MF new file mode 100644 index 000000000..fe2f543c0 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/MANIFEST.MF @@ -0,0 +1,2 @@ +RegistrationClassName: com.sun.star.script.framework.provider.javascript.ScriptProviderForJavaScript +UNO-Type-Path: diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptEditorForJavaScript.java b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptEditorForJavaScript.java new file mode 100644 index 000000000..4cf5cc338 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptEditorForJavaScript.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.script.framework.provider.javascript; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptEditor; +import com.sun.star.script.framework.provider.SwingInvocation; +import com.sun.star.script.provider.XScriptContext; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import java.io.IOException; +import java.io.InputStream; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ImporterTopLevel; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.tools.debugger.Main; +import org.mozilla.javascript.tools.debugger.ScopeProvider; + +public class ScriptEditorForJavaScript extends ScriptEditor { + + // global ScriptEditorForJavaScript instance + private static ScriptEditorForJavaScript theScriptEditorForJavaScript; + + // template for JavaScript scripts + private static String JSTEMPLATE; + + private static Main rhinoWindow; + private URL scriptURL; + // global list of ScriptEditors, key is [external form of URL] of file being edited + private static Map<String, ScriptEditorForJavaScript> BEING_EDITED = new + HashMap<String, ScriptEditorForJavaScript>(); + + static { + JSTEMPLATE = "// JavaScript script"; + try { + URL url = ScriptEditorForJavaScript.class.getResource("template.js"); + if (url != null) { + InputStream in = url.openStream(); + StringBuilder buf = new StringBuilder(); + byte[] b = new byte[1024]; + int len; + + while ((len = in.read(b)) != -1) { + buf.append(new String(b, 0, len)); + } + + in.close(); + + JSTEMPLATE = buf.toString(); + } + } catch (IOException ioe) { + } catch (Exception e) { + } + } + + /** + * Returns the global ScriptEditorForJavaScript instance. + */ + + public static synchronized ScriptEditorForJavaScript getEditor() { + if (theScriptEditorForJavaScript == null) { + theScriptEditorForJavaScript = new ScriptEditorForJavaScript(); + } + + return theScriptEditorForJavaScript; + } + + /** + * Get the ScriptEditorForJavaScript instance for this URL + * + * @param url The URL of the script source file + * + * @return The ScriptEditorForJavaScript associated with + * the given URL if one exists, otherwise null. + */ + public static ScriptEditorForJavaScript getEditor(URL url) { + synchronized (BEING_EDITED) { + return BEING_EDITED.get(url.toExternalForm()); + } + } + + /** + * Returns whether or not the script source being edited in this + * ScriptEditorForJavaScript has been modified + */ + public boolean isModified() { + return rhinoWindow.isModified(scriptURL); + } + + /** + * Returns the text being displayed in this ScriptEditorForJavaScript + * + * @return The text displayed in this ScriptEditorForJavaScript + */ + public String getText() { + return rhinoWindow.getText(scriptURL); + } + + /** + * Returns the Rhino Debugger url of this ScriptEditorForJavaScript + * + * @return The url of this ScriptEditorForJavaScript + */ + public String getURL() { + return scriptURL.toString(); + } + + /** + * Returns the template text for JavaScript scripts + * + * @return The template text for JavaScript scripts + */ + public String getTemplate() { + return JSTEMPLATE; + } + + /** + * Returns the default extension for JavaScript scripts + * + * @return The default extension for JavaScript scripts + */ + public String getExtension() { + return "js"; + } + + /** + * Opens an editor window for the specified ScriptMetaData. + * If an editor window is already open for that data it will be + * moved to the front. + * + * @param context The context in which to execute the script + * @param entry The metadata describing the script + */ + public void edit(final XScriptContext context, ScriptMetaData entry) { + try { + String sUrl = entry.getParcelLocation(); + + if (!sUrl.endsWith("/")) { + sUrl += "/"; + } + + sUrl += entry.getLanguageName(); + final URL url = entry.getSourceURL(); + SwingInvocation.invoke( + new Runnable() { + public void run() { + synchronized (BEING_EDITED) { + ScriptEditorForJavaScript editor = BEING_EDITED.get(url.toExternalForm()); + + if (editor == null) { + editor = new ScriptEditorForJavaScript(context, url); + BEING_EDITED.put(url.toExternalForm(), editor); + } + } + + assert rhinoWindow != null; + rhinoWindow.showScriptWindow(url); + rhinoWindow.toFront(); + } + }); + } catch (IOException e) { + LogUtils.DEBUG("Caught exception: " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + } + + // Ensures that new instances of this class can only be created using + // the factory methods + private ScriptEditorForJavaScript() { + } + + private ScriptEditorForJavaScript(XScriptContext context, URL url) { + setContext(context); + // Need to check that before showing the window. Checking in execute() has no effect. + if (!isMacroExectionEnabled()) { + showErrorMessage("Macro Execution has been disabled."); + return ; + } + initUI(); + Scriptable scope = getScope(context); + rhinoWindow.openFile(url, scope, new CloseHandler(url)); + this.scriptURL = url; + } + + /** + * Executes the script edited by the editor + * + */ + + public Object execute() throws Exception { + rhinoWindow.toFront(); + + return rhinoWindow.runScriptWindow(scriptURL); + } + + /** + * Indicates the line where error occurred + * + */ + public void indicateErrorLine(int lineNum) { + rhinoWindow.toFront(); + rhinoWindow.highlighLineInScriptWindow(scriptURL, lineNum); + } + // This code is based on the main method of the Rhino Debugger Main class + // We pass in the XScriptContext in the global scope for script execution + private void initUI() { + try { + synchronized (ScriptEditorForJavaScript.class) { + if (rhinoWindow != null) { + return; + } + + final Main sdb = new Main("Rhino JavaScript Debugger"); + sdb.pack(); + sdb.setSize(640, 640); + sdb.setVisible(true); + sdb.setExitAction(new Runnable() { + public void run() { + sdb.clearAllBreakpoints(); + sdb.dispose(); + shutdown(); + } + }); + Context.addContextListener(sdb); + sdb.setScopeProvider(new ScopeProvider() { + @Override + public Scriptable getScope() { + return org.mozilla.javascript.tools.shell.Main.getScope(); + } + }); + sdb.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + shutdown(); + } + }); + rhinoWindow = sdb; + } + } catch (Exception exc) { + LogUtils.DEBUG(LogUtils.getTrace(exc)); + } + } + + private void shutdown() { + // dereference Rhino Debugger window + rhinoWindow = null; + this.scriptURL = null; + + // remove all scripts from BEING_EDITED + synchronized (BEING_EDITED) { + java.util.Iterator<String> iter = BEING_EDITED.keySet().iterator(); + java.util.ArrayList<String> keysToRemove = new java.util.ArrayList<String>(); + + while (iter.hasNext()) { + String key = iter.next(); + keysToRemove.add(key); + } + + for (int i = 0; i < keysToRemove.size(); i++) { + BEING_EDITED.remove(keysToRemove.get(i)); + } + + keysToRemove = null; + } + + } + private Scriptable getScope(XScriptContext xsctxt) { + Context ctxt = Context.enter(); + ImporterTopLevel scope = new ImporterTopLevel(ctxt); + + Scriptable jsCtxt = Context.toObject(xsctxt, scope); + scope.put("XSCRIPTCONTEXT", scope, jsCtxt); + + Scriptable jsArgs = Context.toObject(new Object[0], scope); + scope.put("ARGUMENTS", scope, jsArgs); + + Context.exit(); + return scope; + } + + private static class CloseHandler implements Runnable { + + private final URL url; + + private CloseHandler(URL url) { + this.url = url; + } + + public void run() { + synchronized (BEING_EDITED) { + BEING_EDITED.remove(this.url.toExternalForm()); + } + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptProviderForJavaScript.java b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptProviderForJavaScript.java new file mode 100644 index 000000000..9465a646f --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptProviderForJavaScript.java @@ -0,0 +1,330 @@ +/* + * 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.script.framework.provider.javascript; + +import com.sun.star.comp.loader.FactoryHelper; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XModel; + +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.reflection.InvocationTargetException; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ClassLoaderFactory; +import com.sun.star.script.framework.provider.ScriptContext; +import com.sun.star.script.framework.provider.ScriptEditor; +import com.sun.star.script.framework.provider.ScriptProvider; +import com.sun.star.script.provider.ScriptExceptionRaisedException; +import com.sun.star.script.provider.ScriptFrameworkErrorException; +import com.sun.star.script.provider.ScriptFrameworkErrorType; +import com.sun.star.script.provider.XScript; + +import com.sun.star.uno.XComponentContext; + +import java.net.URL; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ImporterTopLevel; +import org.mozilla.javascript.JavaScriptException; +import org.mozilla.javascript.Scriptable; + +public class ScriptProviderForJavaScript { + + public static class ScriptProviderForJavaScript_2 extends ScriptProvider { + + public ScriptProviderForJavaScript_2(XComponentContext ctx) { + super(ctx, "JavaScript"); + } + + @Override + public XScript getScript(/*IN*/String scriptURI) + throws com.sun.star.uno.RuntimeException, ScriptFrameworkErrorException { + ScriptMetaData scriptData = null; + + try { + scriptData = getScriptData(scriptURI); + ScriptImpl script = new ScriptImpl(m_xContext, scriptData, m_xModel, + m_xInvocContext); + return script; + } catch (com.sun.star.uno.RuntimeException re) { + throw new ScriptFrameworkErrorException( + "Failed to create script object: " + re.getMessage(), + null, scriptData.getLanguageName(), language, ScriptFrameworkErrorType.UNKNOWN); + } + } + + @Override + public boolean hasScriptEditor() { + return true; + } + + @Override + public ScriptEditor getScriptEditor() { + return ScriptEditorForJavaScript.getEditor(); + } + } + + /** + * Returns a factory for creating the service. + * 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 used 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( + ScriptProviderForJavaScript.ScriptProviderForJavaScript_2.class.getName())) { + + xSingleServiceFactory = + FactoryHelper.getServiceFactory( + ScriptProviderForJavaScript.ScriptProviderForJavaScript_2.class, + "com.sun.star.script.provider.ScriptProviderForJavaScript", + multiFactory, regKey); + + } + + return xSingleServiceFactory; + } +} + +class ScriptImpl implements XScript { + + private final ScriptMetaData metaData; + private final XComponentContext m_xContext; + private XMultiComponentFactory m_xMultiComponentFactory; + private final XModel m_xModel; + private final XScriptInvocationContext m_xInvocContext; + + ScriptImpl(XComponentContext ctx, ScriptMetaData metaData, XModel xModel, + XScriptInvocationContext xInvocContext) throws + com.sun.star.uno.RuntimeException { + + this.metaData = metaData; + this.m_xContext = ctx; + this.m_xModel = xModel; + this.m_xInvocContext = xInvocContext; + + try { + this.m_xMultiComponentFactory = m_xContext.getServiceManager(); + } catch (Exception e) { + throw new com.sun.star.uno.RuntimeException(e); + } + + LogUtils.DEBUG("ScriptImpl [javascript] script data = " + metaData); + } + + /** + * The invoke method of the ScriptProviderForJavaScript runs the + * JavaScript script specified in the URI + * + * + * + * @param params All parameters; pure, out params are + * undefined in sequence, i.e., the value + * has to be ignored by the callee + * + * @param aOutParamIndex Out indices + * + * @param aOutParam Out parameters + * + * @return The value returned from the function + * being invoked + * + * @throws ScriptFrameworkErrorException If there is no matching script name + * + * + * @throws InvocationTargetException If the running script throws + * an exception this information + * is captured and rethrown as + * ScriptErrorRaisedException or + * ScriptExceptionRaisedException + */ + public Object invoke( + /*IN*/Object[] params, + /*OUT*/short[][] aOutParamIndex, + /*OUT*/Object[][] aOutParam) + throws ScriptFrameworkErrorException, InvocationTargetException { + + // Initialise the out parameters - not used at the moment + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + ClassLoader cl = null; + + try { + cl = ClassLoaderFactory.getURLClassLoader(metaData); + metaData.getSourceURL(); + } catch (java.net.MalformedURLException mfu) { + throw new ScriptFrameworkErrorException( + mfu.getMessage(), null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.MALFORMED_URL); + } + + Context ctxt = null; + + try { + Object result = null; + + ScriptEditorForJavaScript editor = + ScriptEditorForJavaScript.getEditor(metaData.getSourceURL()); + + if (editor != null) { + result = editor.execute(); + + if (result != null && + result.getClass().getName().equals("org.mozilla.javascript.Undefined")) { + // Always return a string + // TODO revisit + return Context.toString(result); + } + + } + + String source; + + if (editor != null && editor.isModified()) { + LogUtils.DEBUG("GOT A MODIFIED SOURCE"); + source = editor.getText(); + } else { + metaData.loadSource(); + source = metaData.getSource(); + + } + + if (source == null || source.length() == 0) { + throw new ScriptFrameworkErrorException( + "Failed to read source data for script", null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.UNKNOWN); + } + + /* Set the context ClassLoader on the current thread to + be our custom ClassLoader. This is the suggested method + for setting up a ClassLoader to be used by the Rhino + interpreter + */ + if (cl != null) { + Thread.currentThread().setContextClassLoader(cl); + } + + // Initialize a Rhino Context object + ctxt = Context.enter(); + + /* The ImporterTopLevel ensures that importClass and + importPackage statements work in Javascript scripts + Make the XScriptContext available as a global variable + to the script + */ + ImporterTopLevel scope = new ImporterTopLevel(ctxt); + + Scriptable jsCtxt = + Context.toObject( + ScriptContext.createContext( + m_xModel, m_xInvocContext, m_xContext, + m_xMultiComponentFactory), + scope); + + scope.put("XSCRIPTCONTEXT", scope, jsCtxt); + + Scriptable jsArgs = Context.toObject(params, scope); + scope.put("ARGUMENTS", scope, jsArgs); + + result = ctxt.evaluateString(scope, source, "<stdin>", 1, null); + result = Context.toString(result); + return result; + } catch (JavaScriptException jse) { + LogUtils.DEBUG("Caught JavaScriptException exception for JavaScript type = " + + jse.getClass()); + String message = jse.getMessage(); + Object wrap = jse.getValue(); + LogUtils.DEBUG("\t message " + message); + LogUtils.DEBUG("\t wrapped type " + wrap.getClass()); + LogUtils.DEBUG("\t wrapped toString " + wrap.toString()); + ScriptExceptionRaisedException se = new + ScriptExceptionRaisedException(message); + se.lineNum = -1; + se.language = "JavaScript"; + se.scriptName = metaData.getLanguageName(); + se.exceptionType = wrap.getClass().getName(); + se.language = metaData.getLanguage(); + LogUtils.DEBUG("ExceptionRaised exception "); + LogUtils.DEBUG("\t message " + se.getMessage()); + LogUtils.DEBUG("\t lineNum " + se.lineNum); + LogUtils.DEBUG("\t language " + se.language); + LogUtils.DEBUG("\t scriptName " + se.scriptName); + raiseEditor(se.lineNum); + throw new InvocationTargetException( + "JavaScript uncaught exception" + metaData.getLanguageName(), null, se); + } catch (Exception ex) { + LogUtils.DEBUG("Caught Exception " + ex); + LogUtils.DEBUG("rethrowing as ScriptFramework error"); + throw new ScriptFrameworkErrorException( + ex.getMessage(), null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.UNKNOWN); + } finally { + if (ctxt != null) { + Context.exit(); + } + } + } + + private void raiseEditor(int lineNum) { + try { + URL sourceUrl = metaData.getSourceURL(); + + ScriptEditorForJavaScript editor = + ScriptEditorForJavaScript.getEditor(sourceUrl); + + if (editor == null) { + editor = ScriptEditorForJavaScript.getEditor(); + + editor.edit( + ScriptContext.createContext(m_xModel, m_xInvocContext, + m_xContext, m_xMultiComponentFactory), + metaData); + + editor = ScriptEditorForJavaScript.getEditor(sourceUrl); + } + + if (editor != null) { + System.out.println("** Have raised IDE for JavaScript, calling indicateErrorLine for line " + + lineNum); + editor.indicateErrorLine(lineNum); + } + } catch (java.net.MalformedURLException ignore) { + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/template.js b/scripting/java/com/sun/star/script/framework/provider/javascript/template.js new file mode 100644 index 000000000..939ba339c --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/template.js @@ -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 . + */ +// Hello World in JavaScript +// Import standard OpenOffice.org API classes. For more information on +// these classes and the OpenOffice.org API, see the OpenOffice.org +// Developers Guide at: +// https://api.libreoffice.org/ + +importClass(Packages.com.sun.star.uno.UnoRuntime); +importClass(Packages.com.sun.star.text.XTextDocument); +importClass(Packages.com.sun.star.text.XText); +importClass(Packages.com.sun.star.text.XTextRange); +importClass(Packages.com.sun.star.frame.XModel); + +// Import XScriptContext class. An instance of this class is available +// to all JavaScript scripts in the global variable "XSCRIPTCONTEXT". This +// variable can be used to access the document for which this script +// was invoked. + +// Methods available are: + +// XSCRIPTCONTEXT.getDocument() returns XModel +// XSCRIPTCONTEXT.getInvocationContext() returns XScriptInvocationContext or NULL +// XSCRIPTCONTEXT.getDesktop() returns XDesktop +// XSCRIPTCONTEXT.getComponentContext() returns XComponentContext + +// For more information on using this class see the scripting +// developer guides at: + +// https://api.libreoffice.org/docs/DevelopersGuide/ScriptingFramework/ScriptingFramework.xhtml + + +oDoc = UnoRuntime.queryInterface(XModel,XSCRIPTCONTEXT.getInvocationContext()); +if ( !oDoc ) + oDoc = XSCRIPTCONTEXT.getDocument(); +xTextDoc = UnoRuntime.queryInterface(XTextDocument,oDoc); +xText = xTextDoc.getText(); +xTextRange = xText.getEnd(); +xTextRange.setString( "Hello World (in JavaScript)" ); |