All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.libreoffice.ext.unohelper.common.Utils Maven / Gradle / Ivy

/*-
 * #%L
 * UNOHelper
 * %%
 * Copyright (C) 2005 - 2023 The Document Foundation
 * %%
 * Licensed under the EUPL, Version 1.1 or – as soon they will be
 * approved by the European Commission - subsequent versions of the
 * EUPL (the "Licence");
 *
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 *
 * http://ec.europa.eu/idabc/eupl5
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 * #L%
 */
package org.libreoffice.ext.unohelper.common;

import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import org.libreoffice.ext.unohelper.util.UnoProperty;

import com.sun.star.script.browse.BrowseNodeTypes;
import com.sun.star.script.browse.XBrowseNode;
import com.sun.star.script.provider.XScript;
import com.sun.star.script.provider.XScriptProvider;
import com.sun.star.uno.RuntimeException;

/**
 * Interne Funktionen
 */
class Utils
{

  public static class FindNode extends XBrowseNodeAndXScriptProvider
  {
    private String location;
    private boolean caseCorrect;

    public FindNode(XBrowseNode xBrowseNode, XScriptProvider xScriptProvider,
        String location, boolean isCaseCorrect)
    {
      super(xBrowseNode, xScriptProvider);
      this.location = location;
      this.caseCorrect = isCaseCorrect;
    }

    public boolean isCaseCorrect()
    {
      return caseCorrect;
    }

    /**
     * Returns true if this isCaseCorrect and fn2 is not, or both
     * have the same isCaseCorrect but this.location occurs earlier
     * in locations than fn2.location.
     */
    public boolean betterMatchThan(Object fn2, String[] locations)
    {
      Utils.FindNode f2 = (Utils.FindNode) fn2;
      if (this.caseCorrect && !f2.caseCorrect)
        return true;
      if (f2.caseCorrect && !this.caseCorrect)
        return false;
      int i = 0;
      int i2 = 0;
      while (i < locations.length && !locations[i].equals(this.location))
        ++i;
      while (i2 < locations.length && !locations[i2].equals(f2.location))
        ++i2;
      return i < i2;
    }
  }

  /**
   * siehe {@link UNO#findBrowseNodeTreeLeafAndScriptProvider(XBrowseNode,
   * String, String, boolean, String[]))}
   *
   * @param xScriptProvider
   *          der zuletzt gesehene xScriptProvider
   * @param nameToFind
   *          der zu suchende Name in seine Bestandteile zwischen den Punkten
   *          zerlegt.
   * @param nameToFindLC
   *          wie nameToFind aber alles lowercase.
   * @param prefix
   *          das Prefix in seine Bestandteile zwischen den Punkten zerlegt.
   * @param prefixLC
   *          wie prefix aber alles lowercase.
   * @param found
   *          Liste von {@link FindNode}s mit dem Ergebnis der Suche (anfangs
   *          leere Liste übergeben). Die Sortierung ist so, dass zuerst alle
   *          case-sensitive Matches (also exakte Matches) aufgeführt sind,
   *          sortiert gemäss location und dann alle case-insensitive Matches
   *          sortiert gemäss location. Falls location == null, so
   *          wird nur nach case-sensitive und case-insenstive sortiert,
   *          innerhalb dieser Gruppen jedoch nicht mehr.
   * @return die Anzahl der Rekursionsstufen, die beendet werden sollen. Zum
   *         Beispiel heisst ein Rückgabewert von 1, dass die aufrufende
   *         Funktion ein return 0 machen soll.
   *
   * @throws UnoHelperException
   */
  public static int findBrowseNodeTreeLeavesAndScriptProviders(BrowseNode node,
      List prefix, List prefixLC, String[] nameToFind,
      String[] nameToFindLC, String[] location, XScriptProvider xScriptProvider,
      List found) throws UnoHelperException
  {
    String name = node.getName();
    String nameLC = name.toLowerCase();

    XScriptProvider xsc = node.as(XScriptProvider.class);
    if (xsc != null)
      xScriptProvider = xsc;

    Iterator iter = node.children();
    if (!iter.hasNext())
    {
      /*
       * Falls der Knoten nicht vom Typ SCRIPT ist, interessiert er uns nicht.
       * Auch wenn wir davon ausgehen können, dass alle Geschwister ebenfalls
       * keine SCRIPTS sind, dürfen wir nicht mehrere Stufen nach oben gehen, da
       * die Geschwister CONTAINER sein können.
       */
      if (node.getType() != BrowseNodeTypes.SCRIPT)
        return 0;

      /*
       * Falls die location des aktuellen Knotens nicht in der erlaubten Liste
       * ist, können wir gleich 2 Ebenen aufsteigen (d.h zur nächsten Library),
       * weil wir davon ausgehen können, dass innerhalb einer Library alle
       * Skripte die selbe Location haben.
       */
      String nodeLocation = getLocation(node);
      if (location != null && !stringInArray(nodeLocation, location))
      {
        return 2;
      }

      /*
       * Wenn das Präfix schon nicht zu nameToFind passt, dann hat es keinen
       * Sinn, alle Skripte des Moduls durchzuiterieren, weil keines davon
       * passen wird. Wir bestimmen, wieviele Rekursionsstufen wir verlassen
       * können. 0 => Präfix passt zur nameToFind 1 => Wir versuchen das nächste
       * Modul in der selben Library, d.h. letzte Präfix-Komponente passt nicht
       * 2 => Wir versuchen die nächste Library, d.h. die letzten 2
       * Präfix-Komponenten passen nicht. Mehr Ebenen zu verlassen erlauben wir
       * nicht, da es möglich sein kann, dass in verschiedenen Libraries sich
       * die Skripte auf verschiedener Ebene befinden. Im Prinzip ist schon die
       * Annahme, dass sich innerhalb einer Library alle Skripte auf der selben
       * Ebene befinden etwas gewagt. Für Basic ist sie sicher richtig, aber OOo
       * erlaubt noch viele andere Skriptsprachen. Technisch gesehen müsste
       * diese Optimierung die Programmiersprache miteinbeziehen. Im Falle von
       * Basic könnte man vermutlich noch aggressiver sein. Im Falle anderer
       * Sprachen müsste man wohl noch konservativer sein.
       */
      int nMPC = 0;
      if (!prefixLC.isEmpty() && nameToFindLC.length >= 2
          && !prefixLC.get(prefixLC.size() - 1)
              .equals(nameToFindLC[nameToFindLC.length - 2]))
      {
        nMPC = 1;
      }
      if (prefixLC.size() >= 2 && nameToFindLC.length >= 3
          && !prefixLC.get(prefixLC.size() - 2)
              .equals(nameToFindLC[nameToFindLC.length - 3]))
      {
        // ACHTUNG! Hier wird nMPC immer auf 2 gesetzt, nicht inkrementiert.
        // Wenn der Libraryname nicht passt ist es egal, ob der Modulname
        // übereinstimmt!
        nMPC = 2;
      }
      if (nMPC > 0)
        return nMPC;

      // If the name doesn't even match case-insensitive, try the next
      // sibling.
      if (!nameLC.equals(nameToFindLC[nameToFindLC.length - 1]))
        return 0;

      boolean isCaseCorrect = true;
      prefix.add(name); // ACHTUNG! Muss nachher wieder entfernt werden
      for (int i = nameToFind.length - 1, j = prefix.size() - 1; i >= 0
          && j >= 0; --i, --j)
      {
        if (!nameToFind[i].equals(prefix.get(j)))
        {
          isCaseCorrect = false;
          break;
        }
      }
      prefix.remove(prefix.size() - 1); // wieder entfernen vor dem nächsten
                                        // return

      Utils.FindNode findNode = new FindNode(node.unwrap(), xScriptProvider,
          nodeLocation, isCaseCorrect);
      ListIterator liter = found.listIterator();
      while (liter.hasNext())
      {
        if (findNode.betterMatchThan(liter.next(), location))
        {
          liter.previous();
          break;
        }
      }
      liter.add(findNode);

      /*
       * ACHTUNG: Wir haben einen passenden Knoten gefunden. Nun könnten wir
       * davon ausgehen, dass es im selben Modul keine weiteren Matches gibt und
       * return 1 machen als Optimierung. Bei BASIC Makros ist dies auch
       * korrekt, aber bei Makros in case-sensitiven Sprachen ist es durchaus
       * möglich, dass im selben Modul mehrere Matches (in unterschiedlicher
       * Gross/Kleinschrift) sind. Mehr als return 1 ist auch bei BASIC nicht
       * drin, weil auch BASIC bei Modul und Bibliotheksnamen case-sensitive
       * ist.
       */
      if ("basic".equalsIgnoreCase(getLanguage(node)))
        return 1;
      else
        return 0;
    } else // if iter.hasNext()
    {
      /*
       * ACHTUNG! Diese Änderungen müssen vor return wieder Rückgängig gemacht
       * werden
       */
      prefix.add(name);
      prefixLC.add(name.toLowerCase());

      while (iter.hasNext())
      {
        BrowseNode child = iter.next();

        int retL = findBrowseNodeTreeLeavesAndScriptProviders(child, prefix,
            prefixLC, nameToFind, nameToFindLC, location, xScriptProvider,
            found);
        if (retL > 0)
        {
          prefix.remove(prefix.size() - 1);
          prefixLC.remove(prefixLC.size() - 1);
          return retL - 1;
        }
      }

      prefix.remove(prefix.size() - 1);
      prefixLC.remove(prefixLC.size() - 1);

    }
    return 0;
  }

  /**
   * Returns true iff array contains a String that is equals to str
   *
   * @author bnk
   */
  private static boolean stringInArray(String str, String[] array)
  {
    for (int i = 0; i < array.length; ++i)
      if (str.equals(array[i]))
        return true;
    return false;
  }

  /**
   * Falls node.URL() == null oder die URL keinen "location=" Teil
   * enthält, so wird "" geliefert, ansonsten der "location=" Teil ohne das
   * führende "location=".
   *
   * @author bnk
   * @throws UnoHelperException
   */
  private static String getLocation(BrowseNode node) throws UnoHelperException
  { // T
    return getUrlComponent(node, "location");
  }

  private static String getLanguage(BrowseNode node) throws UnoHelperException
  {
    return getUrlComponent(node, "language");
  }

  private static String getUrlComponent(BrowseNode node, String id)
      throws UnoHelperException
  {
    String url = node.getURL();
    if (url == null)
      return "";
    int idx = url.indexOf("?" + id + "=");
    if (idx < 0)
      idx = url.indexOf("&" + id + "=");
    if (idx < 0)
      return "";
    idx += 10;
    int idx2 = url.indexOf('&', idx);
    if (idx2 < 0)
      idx2 = url.length();
    return url.substring(idx, idx2);
  }

  /**
   * Wenn provider = null, so wird versucht, einen passenden
   * Provider zu finden.
   *
   * @author bnk
   * @throws UnoHelperException
   */
  static Object executeMacroInternal(String macroName, Object[] args,
      XScriptProvider provider, XBrowseNode root, String[] location)
      throws UnoHelperException
  { // T
    XBrowseNodeAndXScriptProvider o = UNO
        .findBrowseNodeTreeLeafAndScriptProvider(root, "", macroName, false,
            location);

    if (provider == null)
      provider = o.getXScriptProvider();
    XScript script;
    try
    {
      String uri = (String) UnoProperty.getProperty(o.getXBrowseNode(), UnoProperty.URI);
      script = provider.getScript(uri);
    } catch (Exception x)
    {
      throw new RuntimeException(
          "Objekt " + macroName + " nicht gefunden oder ist kein Skript");
    }

    short[][] aOutParamIndex = new short[][] { new short[0] };
    Object[][] aOutParam = new Object[][] { new Object[0] };
    try
    {
      return script.invoke(args, aOutParamIndex, aOutParam);
    } catch (Exception x)
    {
      x.printStackTrace();
      throw new RuntimeException("Fehler bei invoke() von Makro " + macroName);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy