/** @since 3.9.0 */
public class MenuModificationExtension extends AbstractMenuExtension {

  private static final IScoutLogger LOG =
      ScoutLogManager.getLogger(MenuModificationExtension.class);

  private final Class<? extends IMenuModifier<? extends IMenu>> m_menuModifier;

  public MenuModificationExtension(
      Class<? extends IMenu> menuClass,
      IMenuExtensionFilter filter,
      Class<? extends IMenuModifier<? extends IMenu>> menuModifier) {
    super(menuClass, filter);
    if (menuModifier == null) {
      throw new IllegalArgumentException("menuModifier must not be null");
    }
    // check assignability of given menu class along with the type parameter defined on the menu
    // modifier
    Class<?> menuModifierMenuType =
        TypeCastUtility.getGenericsParameterClass(menuModifier, IMenuModifier.class);
    if (menuModifierMenuType == null) {
      LOG.warn(
          "could not determine generic type parameter of menu modifier '"
              + menuModifier.getName()
              + ";");
    } else if (!menuModifierMenuType.isAssignableFrom(menuClass)) {
      throw new IllegalArgumentException(
          "menuClass must be assignalbe to the generic type of given menuModifier. [menuClass: '"
              + menuClass.getName()
              + "', generic type on menuModifier: '"
              + menuModifierMenuType.getName()
              + "'");
    }
    m_menuModifier = menuModifier;
  }

  public Class<? extends IMenuModifier<? extends IMenu>> getMenuModifier() {
    return m_menuModifier;
  }

  @Override
  public boolean accept(Object anchor, Object container, IMenu menu) {
    if (!getMenuClass().isInstance(menu)) {
      return false;
    }
    return super.accept(anchor, container, menu);
  }

  @SuppressWarnings("unchecked")
  public <T extends IMenu> IMenuModifier<T> createMenuModifier() throws ProcessingException {
    try {
      return (IMenuModifier<T>) m_menuModifier.newInstance();
    } catch (Exception e) {
      throw new ProcessingException(
          "Error while instantiating menu modifier '" + m_menuModifier.getName() + "'.", e);
    }
  }
}
Beispiel #2
0
/** Grid (model) layout of split box only visible parts are used */
public class SplitBoxGrid {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(SplitBoxGrid.class);

  private ISplitBox m_splitBox = null;
  private IFormField[] m_fields;
  private int m_gridColumns;
  private int m_gridRows;

  public SplitBoxGrid(ISplitBox splitBox) {
    m_splitBox = splitBox;
  }

  public void validate() {
    // reset
    m_gridColumns = 2;
    m_gridRows = 1;
    ArrayList<IFormField> list = new ArrayList<IFormField>();
    // filter
    for (IFormField f : m_splitBox.getFields()) {
      if (f.isVisible()) {
        list.add(f);
      } else {
        GridData data = GridDataBuilder.createFromHints(f, 1);
        f.setGridDataInternal(data);
      }
    }
    m_fields = list.toArray(new IFormField[list.size()]);
    layoutStatic();
  }

  private void layoutStatic() {
    int x = 0;
    for (int i = 0; i < m_fields.length; i++) {
      GridData data = GridDataBuilder.createFromHints(m_fields[i], 1);
      data.x = x;
      data.y = 0;
      if (data.weightX < 0) {
        data.weightX = data.w;
      }
      m_fields[i].setGridDataInternal(data);
      x = x + data.w;
      m_gridRows = Math.max(m_gridRows, data.h);
    }
    m_gridColumns = x;
  }

  public int getGridColumnCount() {
    return m_gridColumns;
  }

  public int getGridRowCount() {
    return m_gridRows;
  }
}
public final class FormFieldsExtensionPoint {
  private static IScoutLogger LOG = ScoutLogManager.getLogger(FormFieldsExtensionPoint.class);

  private FormFieldsExtensionPoint() {}

  public static IFormFieldExtension[] getFormFieldExtensions() {
    ArrayList<IFormFieldExtension> formFieldExtensionList = new ArrayList<IFormFieldExtension>();
    IExtensionRegistry reg = Platform.getExtensionRegistry();
    IExtensionPoint xp = reg.getExtensionPoint(Activator.PLUGIN_ID, "formfields");
    IExtension[] extensions = xp.getExtensions();
    for (IExtension extension : extensions) {
      IConfigurationElement[] elements = extension.getConfigurationElements();
      for (IConfigurationElement element : elements) {
        String name = element.getAttribute("name");
        boolean active = "true".equalsIgnoreCase(element.getAttribute("active"));
        FormFieldExtension formFieldExt = new FormFieldExtension(name);
        formFieldExt.setContibuterBundleId(extension.getContributor().getName());
        formFieldExt.setActive(active);
        formFieldExt.setScope(getScopePriority(element.getAttribute("scope")));
        formFieldExt.setModelClassName(element.getAttribute("modelClass"));
        formFieldExt.setFactoryClassName(getClassName(element.getChildren("factory"), "class"));
        formFieldExt.setUiClassName(getClassName(element.getChildren("uiClass"), "class"));
        formFieldExtensionList.add(formFieldExt);
      }
    }
    return formFieldExtensionList.toArray(new IFormFieldExtension[formFieldExtensionList.size()]);
  }

  private static String getClassName(IConfigurationElement[] elements, String attribute) {
    String clazzName = null;
    if (elements != null && elements.length == 1) {
      clazzName = elements[0].getAttribute(attribute);
    }
    return clazzName;
  }

  private static int getScopePriority(String scope) {
    int prio = IFormFieldExtension.SCOPE_DEFAULT;
    if (StringUtility.isNullOrEmpty(scope) || scope.equalsIgnoreCase("default")) {
      prio = IFormFieldExtension.SCOPE_DEFAULT;
    } else if (scope.equalsIgnoreCase("global")) {
      prio = IFormFieldExtension.SCOPE_GLOBAL;
    }
    return prio;
  }
}
public class ClientNotificationPollingJob extends ClientAsyncJob {
  private static final IScoutLogger LOG =
      ScoutLogManager.getLogger(ClientNotificationPollingJob.class);

  private long m_pollInterval;
  private boolean m_analyzeNetworkLatency;

  public ClientNotificationPollingJob(
      IClientSession session, long pollInterval, boolean analyzeNetworkLatency) {
    super("Client notification fetcher", session, true);
    updatePollingValues(pollInterval, analyzeNetworkLatency);
  }

  public void updatePollingValues(long pollInterval, boolean analyzeNetworkLatency) {
    m_pollInterval = Math.max(1000L, pollInterval);
    m_analyzeNetworkLatency = analyzeNetworkLatency;
  }

  @Override
  protected IStatus runStatus(IProgressMonitor monitor) {
    IPingService pingService = SERVICES.getService(IPingService.class);
    try {
      // side-effect of every service call (whether ping or any other) is to get
      // client notifications
      pingService.ping("GetClientNotifications");
    } catch (Throwable t) {
      if (LOG.isInfoEnabled()) {
        LOG.info("polling", t);
      }
    }
    if (monitor.isCanceled()) {
      return Status.CANCEL_STATUS;
    } else {
      // re-schedule
      long netLatency = 0L;
      IPerformanceAnalyzerService perf = SERVICES.getService(IPerformanceAnalyzerService.class);
      if (perf != null) {
        netLatency = perf.getNetworkLatency();
      }
      long sleepInterval =
          m_analyzeNetworkLatency ? Math.max(m_pollInterval, 10 * netLatency) : m_pollInterval;
      schedule(sleepInterval);
      return Status.OK_STATUS;
    }
  }
}
/**
 * Adapter to convert a <code>xsd:dateTime</code> to a {@link Date} and vice versa. For both
 * directions, the transformation is in respect to the default timezone accessible by {@link
 * TimeZone#getDefault()}. If no timezone is provided at all, the date is interpreted to be local to
 * the default timezone.
 *
 * <p>The {@link String} provided must correspond to the <code>xsd:dateTime</code> format defined on
 * <a
 * href="http://www.w3.org/TR/xmlschema-2/#dateTime">http://www.w3.org/TR/xmlschema-2/#dateTime</a>.
 * The format was inspired by [ISO 8601] but with timezone information included, because in [ISO
 * 8601], a time is only represented as local time or in relation to UTC (Zulu time).
 *
 * <p>
 *
 * <h2>Definition of xsd:dateTime format</h2>
 *
 * <b> Format: <code>'-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?</code></b>
 *
 * <ul>
 *   <li>'-'? <em>yyyy</em> is a four-or-more digit optionally negative-signed numeral that
 *       represents the year; if more than four digits, leading zeros are prohibited, and '0000' is
 *       prohibited; also note that a plus sign is <b>not</b> permitted);
 *   <li>the remaining '-'s are separators between parts of the date portion;
 *   <li>the first <em>mm</em> is a two-digit numeral that represents the month;
 *   <li><em>dd</em> is a two-digit numeral that represents the day;
 *   <li>'T' is a separator indicating that time-of-day follows;
 *   <li><em>hh</em> is a two-digit numeral that represents the hour; '24' is permitted if the
 *       minutes and seconds represented are zero, and the <code>dateTime</code> value so
 *       represented is the first instant of the following day (the hour property of a <code>
 *       dateTime</code> object cannot have a value greater than 23);
 *   <li>':' is a separator between parts of the time-of-day portion;
 *   <li>the second <em>mm</em> is a two-digit numeral that represents the minute;
 *   <li><em>ss</em> is a two-integer-digit numeral that represents the whole seconds;
 *   <li>'.' <em>s+</em> (if present) represents the fractional seconds;
 *   <li><em>zzzzzz</em> (if present) represents the timezone.
 * </ul>
 */
public class DefaultTimezoneDateAdapter extends XmlAdapter<String, Date> {

  private static final IScoutLogger LOG =
      ScoutLogManager.getLogger(DefaultTimezoneDateAdapter.class);

  public DefaultTimezoneDateAdapter() {}

  @Override
  public String marshal(Date date) throws Exception {
    if (date == null) {
      return null;
    }

    long utcMillis = date.getTime();
    GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault());
    calendar.setTimeInMillis(utcMillis);

    DatatypeFactory factory = DatatypeFactory.newInstance();
    XMLGregorianCalendar xmlCalendar = factory.newXMLGregorianCalendar(calendar);
    return xmlCalendar.toXMLFormat();
  }

  @Override
  public Date unmarshal(String rawValue) throws Exception {
    if (!StringUtility.hasText(rawValue)) {
      return null;
    }

    // local time of given timezone (or default timezone if not applicable)
    DatatypeFactory factory = DatatypeFactory.newInstance();
    XMLGregorianCalendar xmlCalendar = factory.newXMLGregorianCalendar(rawValue);
    GregorianCalendar calendar = xmlCalendar.toGregorianCalendar();
    long utcMillis = calendar.getTimeInMillis();

    // default time
    Calendar defaultTimezoneCalendar = Calendar.getInstance();
    defaultTimezoneCalendar.setTimeInMillis(utcMillis);
    return defaultTimezoneCalendar.getTime();
  }
}
/** convenience subclass of {@link AbstractDateField} with hasDate=true and hasTime=true */
@ClassId("7475d45c-396f-44c5-bb72-4610d980d3ac")
public abstract class AbstractDateTimeField extends AbstractDateField {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(AbstractDateTimeField.class);

  public AbstractDateTimeField() {
    this(true);
  }

  public AbstractDateTimeField(boolean callInitializer) {
    super(callInitializer);
  }

  @Override
  protected boolean getConfiguredHasTime() {
    return true;
  }

  /** UpdateDisplayTextOnModify is not supported for DateTimeField. */
  @Override
  protected final boolean getConfiguredUpdateDisplayTextOnModify() {
    return false;
  }
}
/** Configuration-related utilities. */
public final class ConfigurationUtility {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(ConfigurationUtility.class);

  private ConfigurationUtility() {}

  /**
   * Filters the given class array and sorts the remaining elements according to their {@link Order}
   * annotation.
   *
   * <p>By default, the method throws an {@link IllegalArgumentException} if one of the remaining
   * classes is not annotated by {@link Order}. The behavior can be switched off by setting the
   * system property <code>bsi.debug.innerclass.order</code> to an arbitrary value.
   *
   * @param classes
   * @param filter
   * @return
   * @throws IllegalArgumentException
   */
  @SuppressWarnings("unchecked")
  public static <T> Class<T>[] sortFilteredClassesByOrderAnnotation(
      Class[] classes, Class<T> filter) {
    TreeMap<CompositeObject, Class> orderedClassesMap = new TreeMap<CompositeObject, Class>();
    for (int i = 0; i < classes.length; i++) {
      if (filter.isAssignableFrom(classes[i])) {
        if (classes[i].isAnnotationPresent(Order.class)) {
          Order order = (Order) classes[i].getAnnotation(Order.class);
          orderedClassesMap.put(new CompositeObject(order.value(), i), classes[i]);
        } else {
          if (!classes[i].isAnnotationPresent(Replace.class)) {
            LOG.error("missing @Order annotation: " + classes[i].getName());
          }
          orderedClassesMap.put(new CompositeObject(Double.MAX_VALUE, i), classes[i]);
        }
      }
    }
    return orderedClassesMap.values().toArray(new Class[orderedClassesMap.size()]);
  }

  /** @deprecated use {@link #sortByOrder(Collection)} instead. Will be removed in release 3.10. */
  @Deprecated
  public static <T> Collection<T> sortByOrderAnnotation(Collection<T> list) {
    return sortByOrder(list);
  }

  /**
   * Sorts the elements according to their order:
   *
   * <ol>
   *   <li>If an {@link Order} annotation is present, its {@link Order#value()} is used
   *   <li>If a {@link Replace} annotation is present, the superclass' order is used
   *   <li>If the object implements {@link IOrdered}, {@link IOrdered#getOrder()} is used
   *   <li>Finally, the index in the original collection is used
   * </ol>
   *
   * @since 3.8.1
   */
  public static <T> Collection<T> sortByOrder(Collection<T> list) {
    if (list == null) {
      return null;
    }
    TreeMap<CompositeObject, T> sortMap = new TreeMap<CompositeObject, T>();
    int index = 0;
    for (T element : list) {
      Class<?> c = element.getClass();
      double order;
      Order orderAnnotation;
      while ((orderAnnotation = c.getAnnotation(Order.class)) == null
          && c.isAnnotationPresent(Replace.class)) {
        c = c.getSuperclass();
      }
      if (orderAnnotation != null) {
        order = orderAnnotation.value();
      } else if (element instanceof IOrdered) {
        order = ((IOrdered) element).getOrder();
      } else {
        order = (double) index;
      }
      sortMap.put(new CompositeObject(order, index), element);
      index++;
    }
    return sortMap.values();
  }

  /**
   * Filters the given class array and returns the first occurrence of an instantiable class of
   * filter
   *
   * @param classes
   * @param filter
   * @return first occurrence of filter, might be annotated with {@link InjectFieldTo} or {@link
   *     Replace}
   */
  @SuppressWarnings("unchecked")
  public static <T> Class<T> filterClass(Class[] classes, Class<T> filter) {
    for (Class c : classes) {
      if (filter.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers())) {
        return c;
      }
    }
    return null;
  }

  /**
   * same as {@link #filterClass(Class[], Class)} but ignoring classes with {@link InjectFieldTo}
   * and {@link Replace} annotations
   *
   * @since 3.8.1
   */
  @SuppressWarnings("unchecked")
  public static <T> Class<T> filterClassIgnoringInjectFieldAnnotation(
      Class[] classes, Class<T> filter) {
    for (Class c : classes) {
      if (filter.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers())) {
        if (!isInjectFieldAnnotationPresent(c)) {
          return c;
        }
      }
    }
    return null;
  }

  /**
   * Filters the given class array and returns all occurrences of instantiable classes of filter
   *
   * @param classes
   * @param filter
   * @return all occurrences of filter
   * @since 3.8.1
   */
  @SuppressWarnings("unchecked")
  public static <T> Class<T>[] filterClasses(Class[] classes, Class<T> filter) {
    ArrayList<Class<T>> list = new ArrayList<Class<T>>();
    for (Class c : classes) {
      if (filter.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers())) {
        list.add(c);
      }
    }
    return list.toArray(new Class[0]);
  }

  /**
   * same as {@link #filterClasses(Class[], Class)} but ignoring classes with {@link InjectFieldTo}
   * and {@link Replace} annotations
   *
   * @since 3.8.1
   */
  @SuppressWarnings("unchecked")
  public static <T> Class<T>[] filterClassesIgnoringInjectFieldAnnotation(
      Class[] classes, Class<T> filter) {
    ArrayList<Class<T>> list = new ArrayList<Class<T>>();
    for (Class c : classes) {
      if (filter.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers())) {
        if (!isInjectFieldAnnotationPresent(c)) {
          list.add(c);
        }
      }
    }
    return list.toArray(new Class[0]);
  }

  /**
   * same as {@link #filterClasses(Class[], Class)} but only accepting classes with {@link
   * InjectFieldTo} and {@link Replace} annotations
   *
   * @since 3.8.1
   */
  @SuppressWarnings("unchecked")
  public static <T> Class<T>[] filterClassesWithInjectFieldAnnotation(
      Class[] classes, Class<T> filter) {
    ArrayList<Class<T>> list = new ArrayList<Class<T>>();
    for (Class c : classes) {
      if (filter.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers())) {
        if (isInjectFieldAnnotationPresent(c)) {
          list.add(c);
        }
      }
    }
    return list.toArray(new Class[0]);
  }

  /**
   * @return Returns <code>true</code> if the given class is annotated by {@link InjectFieldTo} or
   *     {@link Replace}. Otherwise <code>false</code>.
   *     <p><b>Note:</b> This method throws a {@link NullPointerException} if the given class is
   *     null.
   */
  public static boolean isInjectFieldAnnotationPresent(Class<?> c) {
    return c.isAnnotationPresent(InjectFieldTo.class) || c.isAnnotationPresent(Replace.class);
  }

  /** get all declared classes (inner types) of the specified class and all its super classes */
  public static Class[] getDeclaredPublicClasses(Class c) {
    return c.getClasses();
  }

  public static <T> T newInnerInstance(Object instance, Class<T> innerClass) throws Exception {
    if (innerClass.getDeclaringClass() != null
        && (innerClass.getModifiers() & Modifier.STATIC) == 0) {
      Constructor<T> c =
          innerClass.getDeclaredConstructor(new Class[] {innerClass.getDeclaringClass()});
      return c.newInstance(new Object[] {instance});
    } else {
      return innerClass.newInstance();
    }
  }

  /** @return true if the declared method is overwritten in implementationType */
  public static boolean isMethodOverwrite(
      Class<?> declaringType,
      String methodName,
      Class[] parameterTypes,
      Class<?> implementationType) {
    try {
      Method declaredMethod;
      try {
        declaredMethod = declaringType.getDeclaredMethod(methodName, parameterTypes);
      } catch (NoSuchMethodException e) {
        LOG.error("cannot find declared method " + declaringType.getName() + "." + methodName, e);
        return false;
      }
      Class<?> c = implementationType;
      while (c != null && c != declaringType) {
        try {
          // check if method is avaliable
          c.getDeclaredMethod(declaredMethod.getName(), declaredMethod.getParameterTypes());
          return true;
        } catch (NoSuchMethodException e) {
          // nop
        }
        // up
        c = c.getSuperclass();
      }
    } catch (Throwable t) {
      LOG.error(
          "declaringType="
              + declaringType
              + ", methodName="
              + methodName
              + ", parameterTypes="
              + parameterTypes
              + ", implementationType="
              + implementationType,
          t);
    }
    return false;
  }

  /**
   * @return Returns the given objects enclosing container type, i.e the first class on the
   *     enclosing classes path that is abstract or the outermost enclosing class. The latter is the
   *     primary type.
   */
  public static Class<?> getEnclosingContainerType(Object o) {
    if (o == null) {
      return null;
    }
    Class<?> c = o.getClass();
    while (!Modifier.isAbstract(c.getModifiers()) && c.getEnclosingClass() != null) {
      c = c.getEnclosingClass();
    }
    return c;
  }

  /**
   * Returns a new array without those classes, that are replaced by another class. The returned
   * array is a new instance, except there are no replacing classes. Replacing classes are annotated
   * with {@link Replace}. Replacing classes are reordered according to their nearest {@link Order}
   * annotation that is found up the type hierarchy.
   *
   * @param classes
   * @return
   * @since 3.8.2
   */
  @SuppressWarnings("unchecked")
  public static <T> Class<? extends T>[] removeReplacedClasses(Class<? extends T>[] classes) {
    Set<Class<? extends T>> replacingClasses = getReplacingLeafClasses(classes);
    if (replacingClasses.isEmpty()) {
      // there are no replacing classes -> return original array
      return classes;
    }

    // compute resulting list of ordered classes
    List<Class<? extends T>> list = new ArrayList<Class<? extends T>>();
    for (Class<? extends T> c : classes) {
      list.add(c);
    }

    for (Class<? extends T> replacingClass : replacingClasses) {
      boolean reorder = !replacingClass.isAnnotationPresent(Order.class);
      boolean reordered = false;

      // handle transitive replacements
      Class<?> classToBeReplaced = replacingClass.getSuperclass();
      while (classToBeReplaced.isAnnotationPresent(Replace.class)) {
        // reorder replacement if necessary
        if (reorder && !reordered && classToBeReplaced.isAnnotationPresent(Order.class)) {
          reordered = moveBefore(list, replacingClass, (Class<? extends T>) classToBeReplaced);
        }
        list.remove(classToBeReplaced);
        classToBeReplaced = classToBeReplaced.getSuperclass();
      }

      // reorder replacement if necessary
      if (reorder && !reordered) {
        moveBefore(list, replacingClass, (Class<? extends T>) classToBeReplaced);
      }
      list.remove(classToBeReplaced);
    }

    return list.toArray(new Class[list.size()]);
  }

  /**
   * Computes a map based on the given classes that contains replaced classes pointing to their
   * replacing classes. This method never returns <code>null</code>.
   *
   * <p><b>Example:</b> Given the following two classes
   *
   * <pre>
   * public class A {
   * }
   *
   * &#064;Replace
   * public class B extends A {
   * }
   * </pre>
   *
   * The invocation of <code>getReplacementMapping(new Class[] {B.class, String.class})</code>
   * returns a map containing <code>&lt;A.class, B.class&gt;</code>.
   *
   * @param classes
   * @return
   * @since 3.8.2
   */
  public static <T> Map<Class<?>, Class<? extends T>> getReplacementMapping(
      Class<? extends T>[] classes) {
    Set<Class<? extends T>> replacingClasses = getReplacingLeafClasses(classes);
    if (replacingClasses.isEmpty()) {
      // there are no replacing classes -> return original array
      return Collections.emptyMap();
    }

    // compute resulting replacement mapping
    Map<Class<?>, Class<? extends T>> mappings = new HashMap<Class<?>, Class<? extends T>>();
    for (Class<? extends T> c : replacingClasses) {
      Class<?> tmpClass = c;
      do {
        tmpClass = tmpClass.getSuperclass();
        mappings.put(tmpClass, c);
      } while (tmpClass.isAnnotationPresent(Replace.class));
    }
    return mappings;
  }

  /**
   * Computes the set of classes that are annotated with {@link Replace} and removes transitive
   * dependencies, so that the most specific classes are returned.
   *
   * <p><b>Example:</b> Given the following two classes
   *
   * <pre>
   * public class A {
   * }
   *
   * &#064;Replace
   * public class B extends A {
   * }
   * </pre>
   *
   * The invocation of <code>getReplacingLeafClasses(new Class[] {A.class, B.class, String.class})
   * </code> returns a set that contains <code>B.class</code> only. <code>String.class</code> is not
   * annotated with {@link Replace} and <code>A.class</code> is not a leaf replacement, but further
   * replaced by <code>B.class</code>.
   *
   * @param classes
   * @return Returns the set of replacing leaf classes or an empty set.
   * @since 3.8.2
   */
  public static <T> Set<Class<? extends T>> getReplacingLeafClasses(Class<? extends T>[] classes) {
    // gather all replacing and replaced classes (i.e. those annotated with @Replace and their super
    // classes)
    Set<Class<? extends T>> replacingClasses = new HashSet<Class<? extends T>>();
    Set<Class<?>> replacedClasses = new HashSet<Class<?>>();
    for (Class<? extends T> c : classes) {
      if (c.isAnnotationPresent(Replace.class)) {
        replacingClasses.add(c);
        Class<?> tmpClass = c;
        do {
          tmpClass = tmpClass.getSuperclass();
          replacedClasses.add(tmpClass);
        } while (tmpClass.isAnnotationPresent(Replace.class));
      }
    }

    if (replacingClasses.isEmpty()) {
      return Collections.emptySet();
    }

    // remove transitive replacements (e.g. if A replaces B and B replaces C, A and B are replacing
    // classes but we are interested in A only)
    replacingClasses.removeAll(replacedClasses);
    return replacingClasses;
  }

  /**
   * Moves the given element before the reference element. Both are expected to be part of the given
   * list. If the reference element is not in the list, the list remains untouched. If the element
   * to move is not part of the list, it is added before the reference element.
   *
   * @param list
   * @param element
   * @param referenceElement
   * @return Returns <code>true</code> if the element has been moved or inserted. Otherwise <code>
   *     false</code>.
   * @since 3.8.2
   */
  private static <T> boolean moveBefore(List<T> list, T element, T referenceElement) {
    int index = list.indexOf(referenceElement);
    if (index != -1) {
      list.remove(element);
      list.add(index, element);
      return true;
    }
    return false;
  }
}
@ClassId("480ea07e-9cec-4591-ba73-4bb9aa45a60d")
public abstract class AbstractImageField extends AbstractFormField implements IImageField {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(AbstractImageField.class);

  private IImageFieldUIFacade m_uiFacade;
  private final EventListenerList m_listenerList = new EventListenerList();
  private IContextMenu m_contextMenu;
  private double m_zoomDelta;
  private double m_panDelta;
  private double m_rotateDelta;

  public AbstractImageField() {
    this(true);
  }

  public AbstractImageField(boolean callInitializer) {
    super(callInitializer);
  }

  @Override
  protected int getConfiguredVerticalAlignment() {
    return 0;
  }

  @Override
  protected int getConfiguredHorizontalAlignment() {
    return 0;
  }

  @ConfigProperty(ConfigProperty.ICON_ID)
  @Order(300)
  protected String getConfiguredImageId() {
    return null;
  }

  @ConfigProperty(ConfigProperty.BOOLEAN)
  @Order(320)
  protected boolean getConfiguredAutoFit() {
    return false;
  }

  @ConfigProperty(ConfigProperty.DOUBLE)
  @Order(330)
  protected double getConfiguredZoomDelta() {
    return 1.25;
  }

  @Override
  @Order(190)
  @ConfigProperty(ConfigProperty.BOOLEAN)
  protected boolean getConfiguredFocusable() {
    return false;
  }

  @ConfigProperty(ConfigProperty.DOUBLE)
  @Order(340)
  protected double getConfiguredPanDelta() {
    return 10;
  }

  /** in degrees 0..360 */
  @ConfigProperty(ConfigProperty.DOUBLE)
  @Order(350)
  protected double getConfiguredRotateDelta() {
    return 10;
  }

  @ConfigProperty(ConfigProperty.BOOLEAN)
  @Order(360)
  protected boolean getConfiguredScrollBarEnabled() {
    return false;
  }

  /**
   * Configures the drop support of this image field.
   *
   * <p>Subclasses can override this method. Default is {@code 0} (no drop support).
   *
   * @return {@code 0} for no support or one or more of {@link IDNDSupport#TYPE_FILE_TRANSFER},
   *     {@link IDNDSupport#TYPE_IMAGE_TRANSFER}, {@link IDNDSupport#TYPE_JAVA_ELEMENT_TRANSFER} or
   *     {@link IDNDSupport#TYPE_TEXT_TRANSFER} (e.g. {@code TYPE_TEXT_TRANSFER |
   *     TYPE_FILE_TRANSFER}).
   */
  @ConfigProperty(ConfigProperty.DRAG_AND_DROP_TYPE)
  @Order(400)
  protected int getConfiguredDropType() {
    return 0;
  }

  /**
   * Configures the drag support of this image field.
   *
   * <p>Subclasses can override this method. Default is {@code 0} (no drag support).
   *
   * @return {@code 0} for no support or one or more of {@link IDNDSupport#TYPE_FILE_TRANSFER},
   *     {@link IDNDSupport#TYPE_IMAGE_TRANSFER}, {@link IDNDSupport#TYPE_JAVA_ELEMENT_TRANSFER} or
   *     {@link IDNDSupport#TYPE_TEXT_TRANSFER} (e.g. {@code TYPE_TEXT_TRANSFER |
   *     TYPE_FILE_TRANSFER}).
   */
  @ConfigProperty(ConfigProperty.DRAG_AND_DROP_TYPE)
  @Order(410)
  protected int getConfiguredDragType() {
    return 0;
  }

  @ConfigOperation
  @Order(500)
  protected TransferObject execDragRequest() throws ProcessingException {
    return null;
  }

  @ConfigOperation
  @Order(510)
  protected void execDropRequest(TransferObject transferObject) throws ProcessingException {}

  protected List<Class<? extends IMenu>> getDeclaredMenus() {
    Class[] dca = ConfigurationUtility.getDeclaredPublicClasses(getClass());
    List<Class<IMenu>> filtered = ConfigurationUtility.filterClasses(dca, IMenu.class);
    return ConfigurationUtility.removeReplacedClasses(filtered);
  }

  @Override
  public List<IKeyStroke> getContributedKeyStrokes() {
    return MenuUtility.getKeyStrokesFromMenus(getMenus());
  }

  @Override
  protected void initConfig() {
    m_uiFacade = new P_UIFacade();
    super.initConfig();
    setImageTransform(new AffineTransformSpec());
    setAutoFit(getConfiguredAutoFit());
    setImageId(getConfiguredImageId());
    setPanDelta(getConfiguredPanDelta());
    setRotateDelta(getConfiguredRotateDelta());
    setZoomDelta(getConfiguredZoomDelta());
    setDragType(getConfiguredDragType());
    setDropType(getConfiguredDropType());
    setScrollBarEnabled(getConfiguredScrollBarEnabled());

    // menus
    List<Class<? extends IMenu>> declaredMenus = getDeclaredMenus();
    List<IMenu> contributedMenus = m_contributionHolder.getContributionsByClass(IMenu.class);
    OrderedCollection<IMenu> menus = new OrderedCollection<IMenu>();
    for (Class<? extends IMenu> menuClazz : declaredMenus) {
      try {
        menus.addOrdered(ConfigurationUtility.newInnerInstance(this, menuClazz));
      } catch (Exception e) {
        SERVICES
            .getService(IExceptionHandlerService.class)
            .handleException(
                new ProcessingException(
                    "error creating instance of class '" + menuClazz.getName() + "'.", e));
      }
    }
    menus.addAllOrdered(contributedMenus);

    try {
      injectMenusInternal(menus);
    } catch (Exception e) {
      LOG.error("error occured while dynamically contributing menus.", e);
    }
    new MoveActionNodesHandler<IMenu>(menus).moveModelObjects();
    m_contextMenu = new FormFieldContextMenu<IImageField>(this, menus.getOrderedList());
    m_contextMenu.setContainerInternal(this);
  }

  @Override
  protected void initFieldInternal() throws ProcessingException {
    super.initFieldInternal();
    // init actions
    ActionUtility.initActions(getMenus());
  }

  /**
   * Override this internal method only in order to make use of dynamic menus<br>
   * Used to add and/or remove menus<br>
   * To change the order or specify the insert position use {@link IMenu#setOrder(double)}.
   *
   * @param menus live and mutable collection of configured menus
   */
  protected void injectMenusInternal(OrderedCollection<IMenu> menus) {}

  /*
   * Runtime
   */

  /** model observer */
  @Override
  public void addImageFieldListener(ImageFieldListener listener) {
    m_listenerList.add(ImageFieldListener.class, listener);
  }

  @Override
  public void removeImageFieldListener(ImageFieldListener listener) {
    m_listenerList.remove(ImageFieldListener.class, listener);
  }

  private void fireZoomRectangle(BoundsSpec r) {
    fireImageBoxEventInternal(new ImageFieldEvent(this, ImageFieldEvent.TYPE_ZOOM_RECTANGLE, r));
  }

  private void fireAutoFit() {
    fireImageBoxEventInternal(new ImageFieldEvent(this, ImageFieldEvent.TYPE_AUTO_FIT));
  }

  private void fireImageBoxEventInternal(ImageFieldEvent e) {
    EventListener[] a = m_listenerList.getListeners(ImageFieldListener.class);
    if (a != null) {
      for (int i = 0; i < a.length; i++) {
        ((ImageFieldListener) a[i]).imageFieldChanged(e);
      }
    }
  }

  @Override
  public Object getImage() {
    return propertySupport.getProperty(PROP_IMAGE);
  }

  @Override
  public void setImage(Object imgObj) {
    propertySupport.setProperty(PROP_IMAGE, imgObj);
  }

  @Override
  public String getImageId() {
    return propertySupport.getPropertyString(PROP_IMAGE_ID);
  }

  @Override
  public void setImageId(String imageId) {
    propertySupport.setPropertyString(PROP_IMAGE_ID, imageId);
  }

  @Override
  public IContextMenu getContextMenu() {
    return m_contextMenu;
  }

  @Override
  public List<IMenu> getMenus() {
    return getContextMenu().getChildActions();
  }

  @Override
  public double getZoomDeltaValue() {
    return m_zoomDelta;
  }

  @Override
  public void setZoomDelta(double d) {
    m_zoomDelta = d;
  }

  @Override
  public double getPanDelta() {
    return m_panDelta;
  }

  @Override
  public void setPanDelta(double d) {
    m_panDelta = d;
  }

  @Override
  public double getRotateDelta() {
    return m_rotateDelta;
  }

  @Override
  public void setRotateDelta(double deg) {
    m_rotateDelta = deg;
  }

  @Override
  public void setRotateDeltaInRadians(double rad) {
    setRotateDelta(Math.toDegrees(rad));
  }

  @Override
  public AffineTransformSpec getImageTransform() {
    return new AffineTransformSpec(
        (AffineTransformSpec) propertySupport.getProperty(PROP_IMAGE_TRANSFORM));
  }

  @Override
  public void setImageTransform(AffineTransformSpec t) {
    propertySupport.setProperty(PROP_IMAGE_TRANSFORM, new AffineTransformSpec(t));
  }

  @Override
  public BoundsSpec getAnalysisRectangle() {
    return (BoundsSpec) propertySupport.getProperty(PROP_ANALYSIS_RECTANGLE);
  }

  @Override
  public void setAnalysisRectangle(BoundsSpec rect) {
    propertySupport.setProperty(PROP_ANALYSIS_RECTANGLE, rect);
  }

  @Override
  public void setAnalysisRectangle(int x, int y, int w, int h) {
    setAnalysisRectangle(new BoundsSpec(x, y, w, h));
  }

  @Override
  public boolean isAutoFit() {
    return propertySupport.getPropertyBool(PROP_AUTO_FIT);
  }

  @Override
  public void setAutoFit(boolean b) {
    propertySupport.setPropertyBool(PROP_AUTO_FIT, b);
  }

  @Override
  public boolean isScrollBarEnabled() {
    return propertySupport.getPropertyBool(PROP_SCROLL_BAR_ENABLED);
  }

  @Override
  public void setScrollBarEnabled(boolean b) {
    propertySupport.setPropertyBool(PROP_SCROLL_BAR_ENABLED, b);
  }

  @Override
  public void setDragType(int dragType) {
    propertySupport.setPropertyInt(PROP_DRAG_TYPE, dragType);
  }

  @Override
  public int getDragType() {
    return propertySupport.getPropertyInt(PROP_DRAG_TYPE);
  }

  @Override
  public void setDropType(int dropType) {
    propertySupport.setPropertyInt(PROP_DROP_TYPE, dropType);
  }

  @Override
  public int getDropType() {
    return propertySupport.getPropertyInt(PROP_DROP_TYPE);
  }

  @Override
  public byte[] getByteArrayValue() {
    Object value = getImage();
    byte[] b = null;
    if (value instanceof byte[]) {
      b = (byte[]) value;
    }
    return b;
  }

  @Override
  public void doAutoFit() {
    fireAutoFit();
  }

  @Override
  public void doZoomRectangle(int x, int y, int w, int h) {
    fireZoomRectangle(new BoundsSpec(x, y, w, h));
  }

  @Override
  public void doPan(double dx, double dy) {
    AffineTransformSpec t = getImageTransform();
    t.dx = dx;
    t.dy = dy;
    setImageTransform(t);
  }

  @Override
  public void doRelativePan(double dx, double dy) {
    AffineTransformSpec t = getImageTransform();
    t.dx = t.dx + dx;
    t.dy = t.dy + dy;
    setImageTransform(t);
  }

  @Override
  public void doZoom(double fx, double fy) {
    AffineTransformSpec t = getImageTransform();
    t.sx = fx;
    t.sy = fy;
    setImageTransform(t);
  }

  @Override
  public void doRelativeZoom(double fx, double fy) {
    AffineTransformSpec t = getImageTransform();
    t.sx = t.sx * fx;
    t.sy = t.sy * fy;
    setImageTransform(t);
  }

  @Override
  public void doRotate(double angle) {
    AffineTransformSpec t = getImageTransform();
    t.angle = angle;
    setImageTransform(t);
  }

  @Override
  public void doRelativeRotate(double angleInDegrees) {
    AffineTransformSpec t = getImageTransform();
    t.angle = t.angle + Math.toRadians(angleInDegrees);
    setImageTransform(t);
  }

  /*
   * UI accessible
   */
  @Override
  public IImageFieldUIFacade getUIFacade() {
    return m_uiFacade;
  }

  private class P_UIFacade implements IImageFieldUIFacade {

    @Override
    public void setImageTransformFromUI(AffineTransformSpec t) {
      setImageTransform(t);
    }

    @Override
    public TransferObject fireDragRequestFromUI() {
      TransferObject t = null;
      try {
        t = interceptDragRequest();
      } catch (ProcessingException e) {
        LOG.warn(null, e);
      }
      return t;
    }

    @Override
    public void fireDropActionFromUi(TransferObject scoutTransferable) {
      try {
        interceptDropRequest(scoutTransferable);
      } catch (ProcessingException e) {
        LOG.warn(null, e);
      }
    }
  } // end private class

  protected final TransferObject interceptDragRequest() throws ProcessingException {
    List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions =
        getAllExtensions();
    ImageFieldDragRequestChain chain = new ImageFieldDragRequestChain(extensions);
    return chain.execDragRequest();
  }

  protected final void interceptDropRequest(TransferObject transferObject)
      throws ProcessingException {
    List<? extends IFormFieldExtension<? extends AbstractFormField>> extensions =
        getAllExtensions();
    ImageFieldDropRequestChain chain = new ImageFieldDropRequestChain(extensions);
    chain.execDropRequest(transferObject);
  }

  protected static class LocalImageFieldExtension<OWNER extends AbstractImageField>
      extends LocalFormFieldExtension<OWNER> implements IImageFieldExtension<OWNER> {

    public LocalImageFieldExtension(OWNER owner) {
      super(owner);
    }

    @Override
    public TransferObject execDragRequest(ImageFieldDragRequestChain chain)
        throws ProcessingException {
      return getOwner().execDragRequest();
    }

    @Override
    public void execDropRequest(ImageFieldDropRequestChain chain, TransferObject transferObject)
        throws ProcessingException {
      getOwner().execDropRequest(transferObject);
    }
  }

  @Override
  protected IImageFieldExtension<? extends AbstractImageField> createLocalExtension() {
    return new LocalImageFieldExtension<AbstractImageField>(this);
  }
}
public abstract class AbstractMemoryPolicy implements IMemoryPolicy {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(AbstractMemoryPolicy.class);

  public static class SearchFormState {
    final String formContentXml;
    final SearchFilter searchFilter;

    public SearchFormState(String xml, SearchFilter filter) {
      formContentXml = xml;
      searchFilter = filter;
    }
  }

  private boolean m_active;
  private final WeakHashMap<IForm, String> m_formToIdentifierMap;
  private final WeakHashMap<ITable, String> m_tableToIdentifierMap;

  private final FormListener m_formListener =
      new FormListener() {
        @Override
        public void formChanged(FormEvent e) throws ProcessingException {
          // auto-detach
          if (!m_active) {
            e.getForm().removeFormListener(m_formListener);
            return;
          }
          String id = m_formToIdentifierMap.get(e.getForm());
          if (id != null) {
            try {
              handlePageFormEvent(e, id);
            } catch (Throwable t) {
              LOG.warn("page form event " + e, t);
            }
          }
        }
      };

  private final TableColumnFilterListener m_tableColumnFilterListener =
      new TableColumnFilterListener() {
        @Override
        public void tableColumnFilterChanged(TableColumnFilterEvent e) throws ProcessingException {
          if (!m_active) {
            e.getColumnFilterManager().removeListener(m_tableColumnFilterListener);
            return;
          }
          String id = m_tableToIdentifierMap.get(e.getTable());
          if (id != null) {
            try {
              handleTableFilterEvent(e, id);
            } catch (Throwable t) {
              LOG.warn("table filter event " + e, t);
            }
          }
        }
      };

  public AbstractMemoryPolicy() {
    m_formToIdentifierMap = new WeakHashMap<IForm, String>();
    m_tableToIdentifierMap = new WeakHashMap<ITable, String>();
  }

  @Override
  public void addNotify() {
    m_active = true;
  }

  @Override
  public void removeNotify() {
    m_active = false;
  }

  /** Attaches listener on page contents */
  @Override
  public void pageCreated(IPage p) throws ProcessingException {
    if (p.getOutline() instanceof AbstractPageField.SimpleOutline) {
      return;
    }
    if (p instanceof IPageWithTable) {
      IPageWithTable<? extends ITable> pt = (IPageWithTable<?>) p;
      ITable table = pt.getTable();
      if (table != null) {
        String pageTableIdentifier = registerPageTable(pt, table);
        if (pageTableIdentifier != null) {
          loadColumnFilterState(table, pageTableIdentifier);
        }
      }
    }
  }

  @Override
  public void pageSearchFormStarted(IPageWithTable<?> p) throws ProcessingException {
    if (p.getOutline() instanceof AbstractPageField.SimpleOutline) {
      return;
    }
    IForm f = p.getSearchFormInternal();
    if (f != null) {
      String pageFormIdentifier = registerPageForm(p, f);
      if (f.isFormOpen()) {
        loadSearchFormState(f, pageFormIdentifier);
      }
    }
  }

  /** @return the identifier for the page form */
  protected String registerPageForm(IPage p, IForm f) {
    String id = createUniqueIdForPage(p, f);
    m_formToIdentifierMap.put(f, id);
    f.removeFormListener(m_formListener);
    f.addFormListener(m_formListener);
    return id;
  }

  /**
   * @return the identifier for the page table or <code>null</code> if the table does not have a
   *     column filter manager.
   */
  protected String registerPageTable(IPage p, ITable t) {
    if (t.getColumnFilterManager() == null) {
      return null;
    }
    String id = createUniqueIdForPage(p, t);
    m_tableToIdentifierMap.put(t, id);
    t.getColumnFilterManager().removeListener(m_tableColumnFilterListener);
    t.getColumnFilterManager().addListener(m_tableColumnFilterListener);
    return id;
  }

  protected String createUniqueIdForPage(IPage p, Object o) {
    if (p == null) {
      return null;
    }
    StringBuilder builder = new StringBuilder();
    createIdForPage(builder, p, o);
    IPage page = p.getParentPage();
    while (page != null) {
      createIdForPage(builder, page, null);
      page = page.getParentPage();
    }
    CRC32 crc = new CRC32();
    crc.update(builder.toString().getBytes());
    return "" + crc.getValue();
  }

  private void createIdForPage(StringBuilder b, IPage page, Object o) {
    b.append("/");
    b.append(page.getClass().getName());
    if (page.getUserPreferenceContext() != null) {
      b.append("/");
      b.append(page.getUserPreferenceContext());
    }
    if (o != null) {
      b.append("/");
      b.append(o.getClass().getName());
    }
    FastBeanInfo pi = new FastBeanInfo(page.getClass(), page.getClass().getSuperclass());
    for (FastPropertyDescriptor prop : pi.getPropertyDescriptors()) {
      if (prop.getReadMethod() != null
          && (Date.class.isAssignableFrom(prop.getPropertyType())
              || Number.class.isAssignableFrom(prop.getPropertyType())
              || String.class.isAssignableFrom(prop.getPropertyType())
              || long.class.isAssignableFrom(prop.getPropertyType()))) {
        // only accept Numbers, Strings or Dates
        try {
          b.append("/");
          b.append(prop.getName());
          b.append("=");
          b.append(prop.getReadMethod().invoke(page, new Object[0]));
        } catch (Exception e) {
          e.printStackTrace();
          // nop - ignore this property
        }
      }
    }
  }

  protected void handlePageFormEvent(FormEvent e, String pageFormIdentifier)
      throws ProcessingException {
    switch (e.getType()) {
      case FormEvent.TYPE_LOAD_COMPLETE:
        {
          // store form state since it was probably reset
          storeSearchFormState(e.getForm(), pageFormIdentifier);
          break;
        }
      case FormEvent.TYPE_STORE_AFTER:
        {
          storeSearchFormState(e.getForm(), pageFormIdentifier);
          break;
        }
    }
  }

  protected void loadSearchFormState(IForm f, String pageFormIdentifier)
      throws ProcessingException {
    // nop
  }

  protected void storeSearchFormState(IForm f, String pageFormIdentifier)
      throws ProcessingException {
    // nop
  }

  protected void handleTableFilterEvent(TableColumnFilterEvent e, String id)
      throws ProcessingException {
    switch (e.getType()) {
      case TableColumnFilterEvent.TYPE_FILTER_ADDED:
      case TableColumnFilterEvent.TYPE_FILTER_CHANGED:
      case TableColumnFilterEvent.TYPE_FILTER_REMOVED:
      case TableColumnFilterEvent.TYPE_FILTERS_RESET:
        storeColumnFilterState(e.getTable(), id);
        break;
    }
  }

  protected void storeColumnFilterState(ITable t, String pageTableIdentifier)
      throws ProcessingException {
    // nop
  }

  protected void loadColumnFilterState(ITable t, String pageTableIdentifier)
      throws ProcessingException {
    // nop
  }

  @Override
  public void afterOutlineSelectionChanged(final IDesktop desktop) {}

  @Override
  public void beforeTablePageLoadData(IPageWithTable<?> page) {}

  @Override
  public void afterTablePageLoadData(IPageWithTable<?> page) {}

  @Override
  public String toString() {
    return getClass().getSimpleName();
  }
}
/** @author mzi */
public class MqttService extends AbstractService implements IMqttService, MqttCallback {
  private static IScoutLogger s_logger = ScoutLogManager.getLogger(MqttService.class);

  private IClientSession m_session;
  private MqttClient m_mqttClient = null;
  private boolean m_isConnected = false;

  @Override
  public void setup(String broker, String clientId) throws ProcessingException {
    try {
      MemoryPersistence persistence = new MemoryPersistence();

      m_session = ClientSession.get();
      m_mqttClient = new MqttClient(broker, clientId, persistence);
      m_mqttClient.setCallback(this);
    } catch (Exception e) {
      throw new ProcessingException(TEXTS.get("MqttClientCreateException"), e);
    }
  }

  @Override
  public void connect(
      String userName,
      String password,
      Boolean clearSession,
      Integer connectionTimeout,
      String lwtTopic,
      String lwtMessage,
      Integer qos,
      Boolean lwtRetained)
      throws ProcessingException {
    if (m_mqttClient == null) {
      throw new ProcessingException("no mqtt client instance available, call method 'setup' first");
    }

    try {
      MqttConnectOptions options =
          getConnectOptions(
              userName,
              password,
              clearSession,
              connectionTimeout,
              lwtTopic,
              lwtMessage,
              qos,
              lwtRetained);
      m_mqttClient.connect(options);
      m_isConnected = true;
    } catch (Exception e) {
      throw new ProcessingException("an exception ocurred while connecting to the mqtt broker", e);
    }
  }

  private MqttConnectOptions getConnectOptions(
      String userName,
      String password,
      Boolean clearSession,
      Integer connectionTimeout,
      String lwtTopic,
      String lwtMessage,
      Integer lwtQos,
      Boolean lwtRetained) {
    MqttConnectOptions connectOpts = new MqttConnectOptions();

    if (!StringUtility.isNullOrEmpty(userName)) {
      connectOpts.setUserName(userName);

      if (!StringUtility.isNullOrEmpty(password)) {
        connectOpts.setPassword(password.toCharArray());
      }
    }

    if (clearSession != null) {
      connectOpts.setCleanSession(clearSession);
    }

    if (connectionTimeout != null) {
      connectOpts.setConnectionTimeout(connectionTimeout);
    }

    if (!StringUtility.isNullOrEmpty(lwtTopic) && !StringUtility.isNullOrEmpty(lwtMessage)) {
      connectOpts.setWill(
          lwtTopic,
          lwtMessage.getBytes(),
          NumberUtility.nvl(lwtQos, 1),
          BooleanUtility.nvl(lwtRetained, false));
    }

    return connectOpts;
  }

  @Override
  public void connectionLost(Throwable t) {
    s_logger.error("connection to mqtt broker lost. reason: " + t);

    m_isConnected = false;

    if (m_session != null) {
      new ClientJob("mqtt connection lost", m_session, true) {

        @Override
        protected void runVoid(IProgressMonitor monitor) throws Exception {
          MessageHandlingService service = SERVICES.getService(MessageHandlingService.class);
          service.handleDisconnect();
        }
      }.schedule();
    } else {
      s_logger.error("client session is null");
    }
  }

  @Override
  public void deliveryComplete(IMqttDeliveryToken t) {
    s_logger.info("delivery of mqtt message completed. deliveryToken=" + t);
  }

  @Override
  public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
    s_logger.info(
        "mqtt message arrived. message "
            + topic
            + ":'"
            + new String(mqttMessage.getPayload())
            + "' qos="
            + mqttMessage.getQos()
            + " retained="
            + mqttMessage.isRetained());

    final String message = new String(mqttMessage.getPayload());
    final int qos = mqttMessage.getQos();
    final boolean retained = mqttMessage.isRetained();
    final Date received = new Date();

    if (m_session != null) {
      // prevent blocking of modal thread
      new ClientAsyncJob("mqtt message arrived (async wrapper)", m_session, true) {
        @Override
        protected void runVoid(IProgressMonitor monitor) throws Exception {
          // implement ui changes in a sync job
          new ClientSyncJob("mqtt message arrived", m_session) {
            @Override
            protected void runVoid(IProgressMonitor monitor1) throws Throwable {
              MessageHandlingService service = SERVICES.getService(MessageHandlingService.class);
              service.handleMessage(topic, message, qos, retained, received);
            }
          }.schedule();
        }
      }.schedule();
    } else {
      s_logger.error("client session is null");
    }
  }

  @Override
  public void disconnect() throws ProcessingException {
    checkConnection();

    try {
      m_mqttClient.disconnect();
      m_isConnected = false;
    } catch (MqttException e) {
      throw new ProcessingException(TEXTS.get("mqttException"), e);
    }
  }

  @Override
  public void publish(String topic, String content, Integer qos, Boolean retained)
      throws ProcessingException {
    checkConnection();

    try {
      MqttMessage message = new MqttMessage(content.getBytes());

      if (qos != null) {
        message.setQos(qos);
      }

      if (retained != null) {
        message.setRetained(retained);
      }

      m_mqttClient.publish(topic, message);
    } catch (Exception e) {
      throw new ProcessingException(TEXTS.get("publishError"), e);
    }

    s_logger.info("message " + topic + ":'" + content + "' successfully published");
  }

  @Override
  public void subscribe(String topicFilter, Integer qos) throws ProcessingException {
    checkConnection();

    try {
      if (qos == null) {
        m_mqttClient.subscribe(topicFilter);
      } else {
        m_mqttClient.subscribe(topicFilter, qos);
      }
    } catch (Exception e) {
      throw new ProcessingException(TEXTS.get("subscribeError"), e);
    }

    s_logger.info("topic " + topicFilter + "' successfully subscribed");
  }

  @Override
  public void unsubscribe(String topicFilter) throws ProcessingException {
    checkConnection();

    try {
      m_mqttClient.unsubscribe(topicFilter);
    } catch (Exception e) {
      throw new ProcessingException(TEXTS.get("unsubscribeError"), e);
    }
  }

  private void checkConnection() throws ProcessingException {
    if (m_mqttClient == null) {
      throw new ProcessingException(TEXTS.get("noClient"));
    }

    if (!m_mqttClient.isConnected()) {
      throw new ProcessingException(TEXTS.get("noConnection"));
    }
  }

  @Override
  public boolean isConnected() throws ProcessingException {
    return m_isConnected;
  }
}
Beispiel #11
0
public class UiDecoration implements IUiDecoration {
  private static IScoutLogger LOG = ScoutLogManager.getLogger(UiDecoration.class);

  private String m_mandatoryFieldBackgroundColor;
  private int m_mandatoryStarMarkerPosition;
  private String m_mandatoryLabelTextColor;
  private FontSpec m_mandatoryLabelFont;
  private int m_dialogMinWidth;
  private int m_dialogMinHeight;
  private int m_formFieldActivationButtonHeight;
  private int m_formFieldActivationButtonWidth;
  private int m_formFieldActivationButtonWithMenuWidth;
  private int m_formFieldLabelWidth;
  private int m_logicalGridLayoutDefaultColumnWidth;
  private int m_logicalGridLayoutHorizontalGap;
  private int m_logicalGridLayoutVerticalGap;
  private int m_logicalGridLayoutRowHeight;
  private int m_processButtonHeight;
  private int m_processButtonMinWidth;
  private int m_processButtonMaxWidth;
  private String m_colorForegroundDisabled;
  private int m_messageBoxMinWidth = 400;
  private int m_messageBoxMinHeight = 100;
  private boolean m_tableMouseMoveSelectionSupportEnabled;
  private boolean m_tableMultilineTooltipSupportEnabled;

  /** one of SWT.RIGHT SWT.LEFT SWT.CENTER */
  private int m_formFieldLabelAlignment = SWT.RIGHT;

  @Override
  public int getDialogMinWidth() {
    return m_dialogMinWidth;
  }

  public void setDialogMinWidth(int dialogMinWidth) {
    m_dialogMinWidth = dialogMinWidth;
  }

  @Override
  public int getDialogMinHeight() {
    return m_dialogMinHeight;
  }

  public void setDialogMinHeight(int dialogMinHeight) {
    m_dialogMinHeight = dialogMinHeight;
  }

  @Override
  public int getProcessButtonHeight() {
    return m_processButtonHeight;
  }

  public void setProcessButtonHeight(int processButtonHeight) {
    m_processButtonHeight = processButtonHeight;
  }

  @Override
  public int getProcessButtonMinWidth() {
    return m_processButtonMinWidth;
  }

  public void setProcessButtonMinWidth(int processButtonMinWidth) {
    m_processButtonMinWidth = processButtonMinWidth;
  }

  @Override
  public int getProcessButtonMaxWidth() {
    return m_processButtonMaxWidth;
  }

  public void setProcessButtonMaxWidth(int processButtonMaxWidth) {
    m_processButtonMaxWidth = processButtonMaxWidth;
  }

  @Override
  public int getFormFieldActivationButtonHeight() {
    return m_formFieldActivationButtonHeight;
  }

  public void setFormFieldActivationButtonHeight(int formFieldActivationButtonHeight) {
    m_formFieldActivationButtonHeight = formFieldActivationButtonHeight;
  }

  @Override
  public int getFormFieldActivationButtonWidth() {
    return m_formFieldActivationButtonWidth;
  }

  public void setFormFieldActivationButtonWidth(int formFieldActivationButtonWidth) {
    m_formFieldActivationButtonWidth = formFieldActivationButtonWidth;
  }

  @Override
  public int getFormFieldActivationButtonWithMenuWidth() {
    return m_formFieldActivationButtonWithMenuWidth;
  }

  public void setFormFieldActivationButtonWithMenuWidth(
      int formFieldActivationButtonWithMenuWidth) {
    m_formFieldActivationButtonWithMenuWidth = formFieldActivationButtonWithMenuWidth;
  }

  @Override
  public String getMandatoryFieldBackgroundColor() {
    return m_mandatoryFieldBackgroundColor;
  }

  public void setMandatoryFieldBackgroundColor(String mandatoryFieldBackgroundColor) {
    m_mandatoryFieldBackgroundColor = mandatoryFieldBackgroundColor;
  }

  @Override
  public int getMandatoryStarMarkerPosition() {
    return m_mandatoryStarMarkerPosition;
  }

  public void setMandatoryStarMarkerPosition(int mandatoryStarMarkerPosition) {
    m_mandatoryStarMarkerPosition = mandatoryStarMarkerPosition;
  }

  @Override
  public String getMandatoryLabelTextColor() {
    return m_mandatoryLabelTextColor;
  }

  public void setMandatoryLabelTextColor(String mandatoryLabelTextColor) {
    m_mandatoryLabelTextColor = mandatoryLabelTextColor;
  }

  @Override
  public FontSpec getMandatoryLabelFont() {
    return m_mandatoryLabelFont;
  }

  public void setMandatoryLabelFont(FontSpec mandatoryLabelFont) {
    m_mandatoryLabelFont = mandatoryLabelFont;
  }

  @Override
  public int getFormFieldLabelWidth() {
    return m_formFieldLabelWidth;
  }

  public void setFormFieldLabelWidth(int formFieldLabelWidth) {
    m_formFieldLabelWidth = formFieldLabelWidth;
  }

  @Override
  public int getLogicalGridLayoutDefaultColumnWidth() {
    return m_logicalGridLayoutDefaultColumnWidth;
  }

  public void setLogicalGridLayoutDefaultColumnWidth(int logicalGridLayoutDefaultColumnWidth) {
    m_logicalGridLayoutDefaultColumnWidth = logicalGridLayoutDefaultColumnWidth;
  }

  @Override
  public int getLogicalGridLayoutHorizontalGap() {
    return m_logicalGridLayoutHorizontalGap;
  }

  public void setLogicalGridLayoutHorizontalGap(int logicalGridLayoutHorizontalGap) {
    m_logicalGridLayoutHorizontalGap = logicalGridLayoutHorizontalGap;
  }

  @Override
  public int getLogicalGridLayoutVerticalGap() {
    return m_logicalGridLayoutVerticalGap;
  }

  public void setLogicalGridLayoutVerticalGap(int logicalGridLayoutVerticalGap) {
    m_logicalGridLayoutVerticalGap = logicalGridLayoutVerticalGap;
  }

  @Override
  public int getLogicalGridLayoutRowHeight() {
    return m_logicalGridLayoutRowHeight;
  }

  public void setLogicalGridLayoutRowHeight(int logicalGridLayoutRowHeight) {
    m_logicalGridLayoutRowHeight = logicalGridLayoutRowHeight;
  }

  @Override
  public String getColorForegroundDisabled() {
    return m_colorForegroundDisabled;
  }

  public void setColorForegroundDisabled(String colorForegroundDisabled) {
    m_colorForegroundDisabled = colorForegroundDisabled;
  }

  @Override
  public int getFormFieldLabelAlignment() {
    return m_formFieldLabelAlignment;
  }

  public void setFormFieldLabelAlignment(int propertyString) {
    m_formFieldLabelAlignment = propertyString;
  }

  @Override
  public int getMessageBoxMinWidth() {
    return m_messageBoxMinWidth;
  }

  public void setMessageBoxMinWidth(int messageBoxMinWidth) {
    m_messageBoxMinWidth = messageBoxMinWidth;
  }

  @Override
  public int getMessageBoxMinHeight() {
    return m_messageBoxMinHeight;
  }

  public void setMessageBoxMinHeight(int messageBoxMinHeight) {
    m_messageBoxMinHeight = messageBoxMinHeight;
  }

  public void setTableMouseMoveSelectionSupportEnabled(
      boolean tableMouseMoveSelectionSupportEnabled) {
    m_tableMouseMoveSelectionSupportEnabled = tableMouseMoveSelectionSupportEnabled;
  }

  @Override
  public boolean isTableMouseMoveSelectionSupportEnabled() {
    return m_tableMouseMoveSelectionSupportEnabled;
  }

  public void setTableMultilineTooltipSupportEnabled(boolean tableMultilineTooltipSupportEnabled) {
    m_tableMultilineTooltipSupportEnabled = tableMultilineTooltipSupportEnabled;
  }

  @Override
  public boolean isTableMultilineTooltipSupportEnabled() {
    return m_tableMultilineTooltipSupportEnabled;
  }
}
Beispiel #12
0
public final class Base64Utility {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(Base64Utility.class);

  private Base64Utility() {}

  private static final char[] BYTE_TO_CHAR;
  private static final int[] CHAR_TO_BYTE;

  static {
    BYTE_TO_CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();

    int[] charToByte = new int[128];
    for (int i = 0; i < BYTE_TO_CHAR.length; i++) {
      charToByte[(int) BYTE_TO_CHAR[i]] = i;
    }
    CHAR_TO_BYTE = charToByte;
  }

  /**
   * Base-64 encodes the supplied block of data. Line wrapping is not applied on output.
   *
   * @param bytes The block of data that is to be Base-64 encoded.
   * @return A <code>String</code> containing the encoded data.
   */
  public static String encode(byte[] bytes) {
    int length = bytes.length;
    if (length == 0) {
      return "";
    }
    StringBuilder buffer = new StringBuilder((int) Math.ceil(length / 3d) * 4);
    int remainder = length % 3;
    length -= remainder;
    int block;
    int i = 0;
    while (i < length) {
      block = ((bytes[i++] & 0xff) << 16) | ((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff);
      buffer.append(BYTE_TO_CHAR[block >>> 18]);
      buffer.append(BYTE_TO_CHAR[(block >>> 12) & 0x3f]);
      buffer.append(BYTE_TO_CHAR[(block >>> 6) & 0x3f]);
      buffer.append(BYTE_TO_CHAR[block & 0x3f]);
    }
    if (remainder == 0) {
      return buffer.toString();
    }
    if (remainder == 1) {
      block = (bytes[i] & 0xff) << 4;
      buffer.append(BYTE_TO_CHAR[block >>> 6]);
      buffer.append(BYTE_TO_CHAR[block & 0x3f]);
      buffer.append("==");
      return buffer.toString();
    }
    block = (((bytes[i++] & 0xff) << 8) | ((bytes[i]) & 0xff)) << 2;
    buffer.append(BYTE_TO_CHAR[block >>> 12]);
    buffer.append(BYTE_TO_CHAR[(block >>> 6) & 0x3f]);
    buffer.append(BYTE_TO_CHAR[block & 0x3f]);
    buffer.append("=");
    return buffer.toString();
  }

  /**
   * Decodes the supplied Base-64 encoded string.
   *
   * @param string The Base-64 encoded string that is to be decoded.
   * @return A <code>byte[]</code> containing the decoded data block.
   */
  public static byte[] decode(String string) {
    int length = string == null ? 0 : string.length();
    if (length == 0) {
      return new byte[0];
    }
    P_Base64InputStream is = new P_Base64InputStream(string);
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();

    int c1, c2, c3, c4;
    try {
      c1 = is.read();
      c2 = is.read();
      c3 = is.read();
      c4 = is.read();
      while (c1 >= 0 || c2 >= 0 || c3 >= 0 || c4 >= 0) {
        int block;
        block =
            ((c1 != -1 ? CHAR_TO_BYTE[c1] : -1) & 0xff) << 18
                | ((c2 != -1 ? CHAR_TO_BYTE[c2] : -1) & 0xff) << 12
                | ((c3 != -1 ? CHAR_TO_BYTE[c3] : -1) & 0xff) << 6
                | ((c4 != -1 ? CHAR_TO_BYTE[c4] : -1) & 0xff);
        buffer.write((byte) (block >>> 16));
        if (c3 != -1) {
          buffer.write((byte) ((block >>> 8) & 0xff));
        }
        if (c4 != -1) {
          buffer.write((byte) (block & 0xff));
        }
        c1 = is.read();
        c2 = is.read();
        c3 = is.read();
        c4 = is.read();
      }
    } catch (IOException e) {
      LOG.error("IOException in Base64Utility.decode()", e);
      return new byte[0];
    } finally {
      try {
        if (is != null) {
          is.close();
        }
      } catch (IOException e) {
        LOG.warn("P_Base64InputStream couldn't be closed.", e);
      }
    }
    return buffer.toByteArray();
  }

  private static class P_Base64InputStream extends InputStream {
    private final String m_buffer;
    private final int m_count;
    private int m_pos = 0;

    public P_Base64InputStream(String base64String) {
      m_buffer = base64String;
      m_count = base64String.length();
    }

    @Override
    public int read() throws IOException {
      while (m_pos < m_count) {
        char ch = m_buffer.charAt(m_pos++);
        if ((ch >= '0' && ch <= '9')
            || (ch >= 'A' && ch <= 'Z')
            || (ch >= 'a' && ch <= 'z')
            || (ch == '+')
            || (ch == '/')) {
          return (ch & 0xFF);
        }
      }
      return -1;
    }
  }
}
Beispiel #13
0
class ViewSplit {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(ViewSplit.class);

  private Set<CellSplit> m_cellSplits;
  private Set<ViewElement> m_leftViews;
  private Set<ViewElement> m_rightViews;

  ViewSplit(Set<CellSplit> cellSplits) {
    m_cellSplits = cellSplits;
  }

  public ViewSplit(
      Set<CellSplit> cellSplits, Set<ViewElement> leftViews, Set<ViewElement> rightViews) {
    m_cellSplits = cellSplits;
    m_leftViews = leftViews;
    m_rightViews = rightViews;
  }

  public Set<CellSplit> getCellSplits() {
    return m_cellSplits;
  }

  public Set<ViewElement> getLeftViews() {
    return m_leftViews;
  }

  public Set<ViewElement> getRightViews() {
    return m_rightViews;
  }

  public int getLocation() {
    int i = 0;
    for (CellSplit s : m_cellSplits) {
      i += s.getLocation();
    }
    i = i / Math.max(1, m_cellSplits.size());
    return i;
  }

  public void setLocation(int loc) {
    if (!isFixed()) {
      for (CellSplit s : m_cellSplits) {
        s.setLocation(loc);
      }
    }
  }

  /**
   * left=top, right=bottom
   *
   * @return amount that was effectively moved
   */
  public int move(int delta, boolean force) {
    if (delta != 0) {
      int limit;
      if (force) {
        limit = Math.abs(delta);
      } else {
        boolean right = delta > 0;
        // check min/max sizes
        int leftSideLimit = 0;
        int rightSideLimit = 0;
        if (!isFixed()) {
          leftSideLimit = 10240;
          rightSideLimit = 10240;
          for (ViewElement v : m_leftViews) {
            int[] /* distance-to-min,distance-to-max*/ bounds =
                getResizeDistances(v, isRowSplit() ? v.top : v.left);
            int distanceToMin = bounds[0];
            int distanceToMax = bounds[1];
            if (right) {
              // use distanceToMax
              leftSideLimit = Math.min(leftSideLimit, distanceToMax);
            } else {
              // use negative distanceToMin
              leftSideLimit = Math.min(leftSideLimit, -distanceToMin);
            }
          }
          for (ViewElement v : m_rightViews) {
            int[] /* distance-to-min,distance-to-max*/ bounds =
                getResizeDistances(v, isRowSplit() ? v.bottom : v.right);
            int distanceToMin = bounds[0];
            int distanceToMax = bounds[1];
            if (right) {
              // use negative distanceToMin
              rightSideLimit = Math.min(rightSideLimit, -distanceToMin);
            } else {
              // use distanceToMax
              rightSideLimit = Math.min(rightSideLimit, distanceToMax);
            }
          }
        }
        limit = Math.min(leftSideLimit, rightSideLimit);
        if (limit > 0) {
          if (right) {
            if (delta > limit) {
              delta = limit;
            }
          } else {
            if (delta < -limit) {
              delta = -limit;
            }
          }
          setLocation(getLocation() + delta);
          return delta;
        } else {
          return 0;
        }
      }
      setLocation(getLocation() + delta);
      return delta;
    }
    return 0;
  }

  public boolean isFixed() {
    for (CellSplit s : m_cellSplits) {
      if (s.isFixed()) {
        return true;
      }
    }
    return false;
  }

  public boolean isRowSplit() {
    for (CellSplit s : m_cellSplits) {
      if (s.isRowSplit()) {
        return true;
      }
    }
    return false;
  }

  private int[] /*distance-to-min,distance-to-max*/ getResizeDistances(
      ViewElement v, ViewSplit oppositeSplit) {
    Component comp = v.getFrame();
    if (comp == null || oppositeSplit == null) {
      return new int[] {0, 0};
    }
    Dimension[] d = SwingLayoutUtility.getValidatedSizes(comp);
    /*
     * ticket 90942, detail pane initially only has 20px height
     * The size of the JInternalFrame is irrelevant and may be different than the effective split size,
     * therefore strictly use the current size of the split distance
     * [old inaccurate: Dimension s = comp.getSize();]
     */
    int splitSize = Math.abs(oppositeSplit.getLocation() - this.getLocation());
    if (isRowSplit()) {
      return getResizeDistances(d[0].height, splitSize, d[2].height);
    } else {
      return getResizeDistances(d[0].width, splitSize, d[2].width);
    }
  }

  private int[] /* distance-to-min,distance-to-max */ getResizeDistances(
      int min, int value, int max) {
    if (value < min) {
      return new int[] {0, max - value};
    } else if (value > max) {
      return new int[] {min - value, 0};
    } else {
      return new int[] {min - value, max - value};
    }
  }

  @Override
  public String toString() {
    return getClass().getSimpleName() + "[" + (isFixed() ? "fixed " : "") + getLocation() + "]";
  }
}
/** @author mzi */
public class BugzillaHtmlFetcher implements IBugFetcher {

  private static final IScoutLogger LOG = ScoutLogManager.getLogger(BugzillaHtmlFetcher.class);

  private String m_criteria = null;
  private String m_assignee = null;
  private String m_product = null;
  private int m_maxNumberOfBugs = 5;

  @Override
  public void setQueryCriteria(String criteria) {
    m_criteria = criteria;
  }

  @Override
  public String getQueryCriteria() {
    return m_criteria;
  }

  @Override
  public void setAssignee(String assignee) {
    m_assignee = assignee;
  }

  @Override
  public String getAssignee() {
    return m_assignee;
  }

  @Override
  public void setProduct(String product) {
    m_product = product;
  }

  @Override
  public String getProduct() {
    return m_product;
  }

  @Override
  public void setMaxNumberOfBugs(int bugs) {
    m_maxNumberOfBugs = bugs;
  }

  @Override
  public int getMaxNumberOfBugs() {
    return m_maxNumberOfBugs;
  }

  @Override
  public List<IBug> fetchBugs() throws ProcessingException {
    List<IBug> bugs = new ArrayList<IBug>();

    String url = buildQueryUrl();
    Element bugTable = getBugTableElement(url, 10000);
    Elements bugElements = getBugElements(bugTable);

    int i = 0;
    for (Element e : bugElements) {
      if (i++ >= getMaxNumberOfBugs()) break;
      bugs.add(createBugFromElement(e, i));
    }

    return bugs;
  }

  private String buildQueryUrl() {
    String url = getQueryCriteria();
    String assignee = StringUtility.nvl(getAssignee(), "").trim();
    String product = StringUtility.nvl(getProduct(), "").trim();

    if (assignee.length() > 0) {
      LOG.info("assignee='" + assignee + "'");
      url += "&emailtype1=substring&emailassigned_to1=1&email1=" + assignee;
    }

    if (product.length() > 0) {
      LOG.info("product='" + product + "'");
      url += "&product=" + product;
    }

    LOG.info("using query url='" + url + "'");

    return url;
  }

  private Element getBugTableElement(String url, int timeout) throws ProcessingException {
    Document doc = null;

    if (StringUtility.isNullOrEmpty(url)) {
      throw new ProcessingException(
          "No bugzilla base query url provided, check your config.ini file");
    }

    try {
      doc = Jsoup.parse(new URL(url), timeout);
      if (doc == null)
        throw new ProcessingException("Empty document received, check your connection");
    } catch (Exception e) {
      throw new ProcessingException(
          "Exception ", "check your connection and url: '" + url + "'", e);
    }

    return doc.getElementsByClass("bz_buglist").first();
  }

  private Elements getBugElements(Element bugTable) {
    if (bugTable == null) return new Elements();
    else return bugTable.getElementsByClass("bz_bugitem");
  }

  /**
   * columns are parsed out or bugzilla html response. this implies that column list provided in the
   * criteria url and the parsing implemented in this method must correspond. see the config.ini
   * entry
   * org.eclipsescout.demo.ibug.server.services.DesktopService#criteria=https://bugs.eclipse.org/bugs/...
   */
  private IBug createBugFromElement(Element e, int i) {
    IBug bug = new BugzillaBug();
    Elements columnElements = e.getElementsByTag("td");

    bug.setId(getColumnTextContent(columnElements, 0));
    bug.setSeverety(getColumnTextContent(columnElements, 1));
    bug.setPriority(getColumnTextContent(columnElements, 2));
    bug.setTargetMilestone(getColumnTextContent(columnElements, 3));
    bug.setStatus(getColumnTextContent(columnElements, 4));
    bug.setResolution(getColumnTextContent(columnElements, 5));
    bug.setComponent(getColumnTextContent(columnElements, 6));
    bug.setAssignee(getColumnTextContent(columnElements, 7));
    bug.setSummary(getColumnTextContent(columnElements, 8));
    bug.setChanged(getColumnTextContent(columnElements, 9));
    bug.setSortValue(i);

    return bug;
  }

  private String getColumnTextContent(Elements rowElements, int i) {
    Element content = rowElements.get(i);
    Element a = content.getElementsByTag("a").first();
    Element span = content.getElementsByTag("span").first();
    StringBuffer text = new StringBuffer();

    text.append(content.ownText());
    if (a != null) text.append(a.ownText());
    if (span != null) text.append(span.ownText());

    return text.toString();
  }
}
Beispiel #15
0
/**
 *
 *
 * <h3>BrowserSupport</h3>
 *
 * adding hyperlink callback support as in normal swt to the rwt browser
 *
 * <p>Adding support for registering/unregistering (publishing) local resources.
 *
 * @author imo
 * @since 3.8.0
 */
public class BrowserExtension {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(BrowserExtension.class);
  private static final Pattern LOCAL_URL_PATTERN =
      Pattern.compile(
          "(['\"])(http://local[?/][^'\"]*)(['\"])", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);

  private final Browser m_browser;
  private final HashMap<String, String> m_hyperlinkMap;
  private final String m_serviceHandlerId;
  private ServiceHandler m_serviceHandler;
  private ServerPushSession m_pushSession;
  //
  private HashSet<String> m_tempFileNames = new HashSet<String>();

  public BrowserExtension(Browser b) {
    m_browser = b;
    m_hyperlinkMap = new HashMap<String, String>();
    m_serviceHandlerId = UUID.randomUUID().toString();
  }

  /** @return the unique {@link UUID} serviceHandlerId */
  public String getServiceHandlerId() {
    return m_serviceHandlerId;
  }

  // TODO RAP 2.0 migration - old code
  //  private String getUiCallbackId() {
  //    return getClass().getName() + "" + hashCode();
  //  }

  public void attach() {
    if (m_serviceHandler == null) {
      // TODO RAP 2.0 migration - old code
      // old code       UICallBack.activate(getUiCallbackId());
      m_pushSession = new ServerPushSession();
      m_pushSession.start();
      m_serviceHandler =
          new ServiceHandler() {
            @Override
            public void service(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
              String localUrl = m_hyperlinkMap.get(request.getParameter("p"));
              if (localUrl == null) {
                return;
              }
              fireLocationChangedEvent(localUrl);
            }
          };
      RWT.getServiceManager().registerServiceHandler(m_serviceHandlerId, m_serviceHandler);
    }
  }

  public void detach() {
    // TODO RAP 2.0 migration - old code
    // old code   UICallBack.deactivate(getUiCallbackId());
    m_pushSession.stop();
    clearLocalHyperlinkCache();
    clearResourceCache();
    if (m_serviceHandler != null) {
      m_serviceHandler = null;
      RWT.getServiceManager().unregisterServiceHandler(m_serviceHandlerId);
    }
  }

  /**
   * Adds a text resource that is encoded with the given <code>charset</code>.
   *
   * <p>By specifying an <code>option</code> other than <code>NONE</code> the resource will be
   * versioned and/or compressed. As compressing is only intended for resources that contain
   * JavaScript, versioning might be useful for other resources as well. When versioning is enabled
   * a version number is appended to the resources' name which is derived from its content.
   *
   * <p>
   *
   * @param content the content of the resource to add.
   * @return the web url of the resource valid for calls from outside
   */
  public String addResource(String name, InputStream content) {
    name = name.replaceAll("\\\\", "/");
    if (name == null || name.length() == 0) {
      return null;
    }
    if (!name.startsWith("/")) {
      name = "/" + name;
    }
    String uniqueName = m_serviceHandlerId + name;
    m_tempFileNames.add(uniqueName);
    ResourceManager resourceManager = RWT.getResourceManager();
    resourceManager.register(uniqueName, content);
    return resourceManager.getLocation(uniqueName);
  }

  public void clearResourceCache() {
    ResourceManager resourceManager = RWT.getResourceManager();
    try {
      for (String name : m_tempFileNames) {
        resourceManager.unregister(name);
      }
    } finally {
      m_tempFileNames.clear();
    }
  }

  /**
   * @param html replaces all http://local/... urls by a ajax callback with a {@link LocationEvent}
   *     in html text
   * @param childDepth when the document is inside a iframe or thelike, then childDepth is 1, if it
   *     is in addition inside an embed tag (such as svg), then childDepth is 2.
   */
  public String adaptLocalHyperlinks(String html, int childDepth) {
    String p;
    if (childDepth <= 0) {
      p = "this";
    } else {
      p = "parent";
      for (int i = 1; i < childDepth; i++) {
        p = "parent." + p;
      }
    }
    return rewriteLocalHyperlinks(html, p, m_serviceHandlerId, m_hyperlinkMap);
  }

  public void clearLocalHyperlinkCache() {
    m_hyperlinkMap.clear();
  }

  private void fireLocationChangedEvent(final String location) {

    m_browser
        .getDisplay()
        .asyncExec(
            new Runnable() {
              @Override
              public void run() {
                try {
                  Constructor<?> c = LocationEvent.class.getDeclaredConstructor(Object.class);
                  c.setAccessible(true);
                  // send changing
                  LocationEvent event = (LocationEvent) c.newInstance(new Event());
                  event.location = location;
                  event.top = true;

                  // send changing
                  final Listener[] locationChangingListeners =
                      m_browser.getListeners(EventTypes.LOCALTION_CHANGING);
                  if (locationChangingListeners != null) {
                    for (Listener l : locationChangingListeners) {
                      if (l instanceof LocationListener) {
                        ((LocationListener) l).changing(event);
                      }
                    }
                  }
                  // send changed
                  final Listener[] locationChangedListeners =
                      m_browser.getListeners(EventTypes.LOCALTION_CHANGED);
                  if (locationChangedListeners != null) {
                    for (Listener l : locationChangedListeners) {
                      if (l instanceof LocationListener) {
                        ((LocationListener) l).changed(event);
                      }
                    }
                  }

                } catch (Throwable t) {
                  // nop
                }
              }
            });
  }

  /**
   * Replace all href="http://local/... references in the html file and replace by an ajax call.
   *
   * @param html
   * @param rwtServiceHandler is called with the parameter "p" containing the local url key to the
   *     generatedMapping
   * @param generatedMappings is being filled up with the generated mappings
   * @return the rewritten html
   */
  private static String rewriteLocalHyperlinks(
      String html,
      String ajaxParentContext,
      String rwtServiceHandler,
      Map<String /*externalKey*/, String /*url*/> generatedMappings) {
    if (html == null) {
      return html;
    }
    StringBuilder buf = new StringBuilder();
    Matcher m = LOCAL_URL_PATTERN.matcher(html);
    int nextFind = 0;
    while (m.find(nextFind)) {
      String localUrl = m.group(2);
      String externalKey = "" + generatedMappings.size();
      StringBuilder urlBuf = new StringBuilder();
      urlBuf.append("?");
      urlBuf.append("nocache='+new Date().getTime()+'");
      urlBuf.append("&amp;");
      urlBuf.append("custom_service_handler");
      urlBuf.append("=");
      urlBuf.append(rwtServiceHandler);
      urlBuf.append("&amp;");
      urlBuf.append("p");
      urlBuf.append("=");
      urlBuf.append(externalKey);
      String encodedURL = RWT.getResponse().encodeURL(urlBuf.toString());
      String callableURL =
          "javascript:a="
              + ajaxParentContext
              + ".qx.net.HttpRequest.create();a.open('GET','"
              + encodedURL
              + "',true);a.send(null);";
      buf.append(html.substring(nextFind, m.start()));
      buf.append(m.group(1));
      buf.append(callableURL);
      buf.append(m.group(3));
      // register
      generatedMappings.put(externalKey, localUrl);
      // next
      nextFind = m.end();
    }
    if (nextFind == 0) {
      return html;
    }
    if (nextFind < html.length()) {
      buf.append(html.substring(nextFind));
    }
    return buf.toString();
  }
}
Beispiel #16
0
/**
 * Dynamic code service for testing purposes. Arbitrary code types can be registered dynamically.
 * Consumers must register and remove the service themselves.
 *
 * <p><b>Example</b>:
 *
 * <pre>
 * List<ServiceRegistration> reg = TestingUtility.registerServices(Activator.getDefault().getBundle(), 1000, new TestingCodeService(new MyCodeType()));
 * CODES.getCodeType(MyCodeType.class);
 * [...]
 * SERVICES.getService(TestingCodeService.class).addCodeTypes(new OtherCodeType());
 * [..]
 * TestingUtility.unregisterServices(reg);
 * </pre>
 *
 * @since 3.8.0
 */
public class TestingCodeService extends AbstractService implements ICodeService {

  private static final IScoutLogger LOG = ScoutLogManager.getLogger(TestingCodeService.class);

  private final Map<Class<? extends ICodeType>, ICodeType<?>> m_codeTypes;
  private final Object m_codeTypeMapLock;

  public TestingCodeService(ICodeType<?>... codeTypes) {
    m_codeTypes = new HashMap<Class<? extends ICodeType>, ICodeType<?>>();
    m_codeTypeMapLock = new Object();
    addCodeTypes(codeTypes);
  }

  public void addCodeTypes(ICodeType<?>... codeTypes) {
    synchronized (m_codeTypeMapLock) {
      for (ICodeType<?> ct : codeTypes) {
        if (ct != null) {
          m_codeTypes.put(ct.getClass(), ct);
        }
      }
    }
  }

  @Override
  @SuppressWarnings("unchecked")
  public <T extends ICodeType> T getCodeType(Class<T> type) {
    synchronized (m_codeTypeMapLock) {
      return (T) m_codeTypes.get(type);
    }
  }

  @Override
  public <T extends ICodeType> T getCodeType(Long partitionId, Class<T> type) {
    synchronized (m_codeTypeMapLock) {
      return getCodeType(type);
    }
  }

  @Override
  public ICodeType findCodeTypeById(Object id) {
    synchronized (m_codeTypeMapLock) {
      for (ICodeType<?> ct : m_codeTypes.values()) {
        if (CompareUtility.equals(ct.getId(), id)) {
          return ct;
        }
      }
      return null;
    }
  }

  @Override
  public ICodeType findCodeTypeById(Long partitionId, Object id) {
    synchronized (m_codeTypeMapLock) {
      return findCodeTypeById(id);
    }
  }

  @Override
  public ICodeType[] getCodeTypes(Class... types) {
    synchronized (m_codeTypeMapLock) {
      List<ICodeType> result = new ArrayList<ICodeType>();
      for (Class type : types) {
        @SuppressWarnings("unchecked")
        ICodeType ct = getCodeType(type);
        if (ct != null) {
          result.add(ct);
        }
      }
      return result.toArray(new ICodeType[result.size()]);
    }
  }

  @Override
  public ICodeType[] getCodeTypes(Long partitionId, Class... types) {
    synchronized (m_codeTypeMapLock) {
      return getCodeTypes(types);
    }
  }

  @Override
  @SuppressWarnings("unchecked")
  public <T extends ICode> T getCode(final Class<T> type) {
    synchronized (m_codeTypeMapLock) {
      if (type == null) {
        return null;
      }
      Class declaringCodeTypeClass = null;
      if (type.getDeclaringClass() != null) {
        // code is inner type of code type or another code
        Class c = type.getDeclaringClass();
        while (c != null && !(ICodeType.class.isAssignableFrom(c))) {
          c = c.getDeclaringClass();
        }
        declaringCodeTypeClass = c;
      }
      if (declaringCodeTypeClass == null) {
        try {
          declaringCodeTypeClass = type.newInstance().getCodeType().getClass();
        } catch (Throwable t) {
          LOG.error("find code " + type, t);
        }
      }
      ICodeType codeType = getCodeType(declaringCodeTypeClass);
      final Holder<ICode> codeHolder = new Holder<ICode>(ICode.class);
      ICodeVisitor v =
          new ICodeVisitor() {
            @Override
            public boolean visit(ICode code, int treeLevel) {
              if (code.getClass() == type) {
                codeHolder.setValue(code);
                return false;
              }
              return true;
            }
          };
      codeType.visit(v);
      return (T) codeHolder.getValue();
    }
  }

  @Override
  public <T extends ICode> T getCode(Long partitionId, Class<T> type) {
    synchronized (m_codeTypeMapLock) {
      return getCode(type);
    }
  }

  @Override
  public <T extends ICodeType> T reloadCodeType(Class<T> type) {
    synchronized (m_codeTypeMapLock) {
      LOG.warn("reloading code types is not supported by this testing ICodeService");
      return getCodeType(type);
    }
  }

  @Override
  public ICodeType[] reloadCodeTypes(Class... types) {
    synchronized (m_codeTypeMapLock) {
      LOG.warn("reloading code types is not supported by this testing ICodeService");
      return getCodeTypes(types);
    }
  }

  @Override
  public BundleClassDescriptor[] getAllCodeTypeClasses(String classPrefix) {
    synchronized (m_codeTypeMapLock) {
      List<BundleClassDescriptor> result = new ArrayList<BundleClassDescriptor>();
      for (Class<? extends ICodeType> type : m_codeTypes.keySet()) {
        Bundle bundle = FrameworkUtil.getBundle(type);
        result.add(new BundleClassDescriptor(bundle.getSymbolicName(), type.getName()));
      }
      return result.toArray(new BundleClassDescriptor[result.size()]);
    }
  }

  @Override
  public ICodeType[] getAllCodeTypes(String classPrefix) {
    synchronized (m_codeTypeMapLock) {
      List<ICodeType> result = new ArrayList<ICodeType>();
      for (ICodeType ct : m_codeTypes.values()) {
        if (ct.getClass().getName().startsWith(classPrefix)) {
          result.add(ct);
        }
      }
      return result.toArray(new ICodeType[result.size()]);
    }
  }

  @Override
  public ICodeType[] getAllCodeTypes(String classPrefix, Long partitionId) {
    synchronized (m_codeTypeMapLock) {
      return getAllCodeTypes(classPrefix);
    }
  }
}
/**
 * UI model customization wrapping a {@link org.eclipse.core.runtime.Preferences} object with its
 * location Stored in user area.
 *
 * <p>Warning: Only use this class within a ClientJob with an {@link IClientSession}.
 *
 * <p>Calling from outside a {@link IClientSession} {@link org.eclipse.scout.rt.client.ClientJob
 * ClientJob} will produce a warning. In release 3.9 (TODO) will produce an error.
 */
public class ClientUIPreferences {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(ClientUIPreferences.class);

  /**
   * @return a new instance of the {@link ClientUIPreferences} based on the {@link
   *     IUserPreferencesStorageService}
   *     <p>Warning: Only use this class within a ClientJob with an {@link IClientSession}.
   *     <p>Calling from outside a {@link IClientSession} {@link
   *     org.eclipse.scout.rt.client.ClientJob ClientJob} will produce a warning. Starting with
   *     release 3.9 it will fail with an error.
   */
  public static ClientUIPreferences getInstance() {
    return new ClientUIPreferences(ClientSessionThreadLocal.get());
  }

  /**
   * @return a new instance of the {@link ClientUIPreferences} based on the {@link
   *     IUserPreferencesStorageService}
   */
  public static ClientUIPreferences getInstance(IClientSession session) {
    return new ClientUIPreferences(session);
  }

  private static final String TABLE_CUSTOMIZER_DATA = "table.customizer.data.";
  private static final String TABLE_COLUMN_UIINDEX = "table.column.viewIndex.";
  private static final String TABLE_COLUMN_WIDTH = "table.column.width.";
  private static final String TABLE_COLUMN_VISIBLE = "table.column.visible.";
  private static final String TABLE_COLUMN_SORT_INDEX = "table.column.sortIndex.";
  private static final String TABLE_COLUMN_SORT_ASC = "table.column.sortAsc.";
  private static final String TABLE_COLUMN_SORT_EXPLICIT = "table.column.sortExplicit.";
  private static final String APPLICATION_WINDOW_MAXIMIZED = "application.window.maximized";
  private static final String APPLICATION_WINDOW_BOUNDS = "application.window.bounds";
  private static final String CALENDAR_DISPLAY_MODE = "calendar.display.mode";
  private static final String CALENDAR_DISPLAY_CONDENSED = "calendar.display.condensed";
  private static final String DESKTOP_COLUMN_SPLITS = "desktop.columnSplits";
  private static final String FORM_BOUNDS = "form.bounds.";

  private final IClientSession m_session;
  private Preferences m_env;

  private ClientUIPreferences(IClientSession session) {
    m_session = session;
    if (m_session == null) {
      LOG.error(
          "No scout client session context",
          new Exception("Calling client preferences from outside a scout client session job"));
    }
    load();
  }

  /**
   * Since this property depends on the user agent it is saved separately for each combination of
   * {@link org.eclipse.scout.rt.shared.ui.IUiLayer IUiLayer} and {@link
   * org.eclipse.scout.rt.shared.ui.IUiDeviceType IUiDeviceType}.
   */
  public Rectangle getFormBounds(IForm form) {
    String key = form.computeCacheBoundsKey();
    if (key == null) {
      return null;
    }

    key = getUserAgentPrefix() + FORM_BOUNDS + key;
    String value = m_env.get(key, "");
    if (StringUtility.isNullOrEmpty(value)) {
      key = getLegacyFormBoundsKey(form);
      value = m_env.get(key, "");
    }

    if (!StringUtility.isNullOrEmpty(value)) {
      try {
        StringTokenizer tok = new StringTokenizer(value, ",");
        Rectangle r =
            new Rectangle(
                new Integer(tok.nextToken()).intValue(),
                new Integer(tok.nextToken()).intValue(),
                new Integer(tok.nextToken()).intValue(),
                new Integer(tok.nextToken()).intValue());
        return r;
      } catch (Exception e) {
        LOG.warn("value=" + value, e);
      }
    }
    return null;
  }

  /**
   * Since this property depends on the user agent it is saved separately for each combination of
   * {@link org.eclipse.scout.rt.shared.ui.IUiLayer IUiLayer} and {@link
   * org.eclipse.scout.rt.shared.ui.IUiDeviceType IUiDeviceType}.
   */
  public void setFormBounds(IForm form, Rectangle bounds) {
    String key = form.computeCacheBoundsKey();
    if (key == null) {
      return;
    }

    key = getUserAgentPrefix() + FORM_BOUNDS + key;
    if (bounds == null) {
      m_env.remove(key);
    } else {
      m_env.put(key, bounds.x + "," + bounds.y + "," + bounds.width + "," + bounds.height);
    }
    flush();
  }

  private String getLegacyFormBoundsKey(IForm form) {
    String key = form.computeCacheBoundsKey();
    if (key == null) {
      return null;
    }

    // Add prefix only if not already added.
    // This is mainly necessary due to backward compatibility because until 3.8.0 the prefix had to
    // be returned by computeCacheBoundsKey
    if (!key.startsWith("form.bounds")) {
      key = "form.bounds_" + key;
    }

    // Explicitly don't consider user agent because before 3.8.0 there was no user agent and
    // therefore the keys didn't contain this information.

    return key;
  }

  private String getUserAgentPrefix() {
    UserAgent currentUserAgent = null;
    if (m_session != null) {
      currentUserAgent = m_session.getUserAgent();
    } else {
      currentUserAgent = UserAgentUtility.getCurrentUserAgent();
    }
    if (currentUserAgent == null) {
      return "";
    }

    String uiLayer = null;
    if (!UiLayer.UNKNOWN.equals(currentUserAgent.getUiLayer())) {
      uiLayer = currentUserAgent.getUiLayer().getIdentifier();
    }
    String uiDeviceType = null;
    if (!UiDeviceType.UNKNOWN.equals(currentUserAgent.getUiDeviceType())) {
      uiDeviceType = currentUserAgent.getUiDeviceType().getIdentifier();
    }

    return StringUtility.concatenateTokens(uiLayer, ".", uiDeviceType, ".");
  }

  /**
   * Since this property depends on the user agent it is saved separately for each combination of
   * {@link org.eclipse.scout.rt.shared.ui.IUiLayer IUiLayer} and {@link
   * org.eclipse.scout.rt.shared.ui.IUiDeviceType IUiDeviceType}.
   *
   * @since 3.8.0
   */
  public int[] getSplitterPosition(ISplitBox splitBox) {
    String baseKey = splitBox.getCacheSplitterPositionPropertyName();
    if (baseKey == null) {
      return null;
    }

    String key = getUserAgentPrefix() + baseKey;
    int[] value = getPropertyIntArray(key);
    if (value == null) {
      // If no value has been found try to load with the base key. Done due to backward
      // compatibility.
      value = getPropertyIntArray(baseKey);
    }

    return value;
  }

  /**
   * Since this property depends on the user agent it is saved separately for each combination of
   * {@link org.eclipse.scout.rt.shared.ui.IUiLayer IUiLayer} and {@link
   * org.eclipse.scout.rt.shared.ui.IUiDeviceType IUiDeviceType}.
   *
   * @since 3.8.0
   */
  public void setSplitterPosition(ISplitBox splitBox, int[] weights) {
    String key = splitBox.getCacheSplitterPositionPropertyName();
    if (key == null) {
      return;
    }

    key = getUserAgentPrefix() + key;
    setPropertyIntArray(key, weights);
  }

  public String getTableKey(ITable t) {
    String key = t.getClass().getName();
    String context = t.getUserPreferenceContext();
    if (context != null) {
      key += "#" + context;
    }
    return key;
  }

  public Object getTableCustomizerData(String customizerKey) {
    String key = TABLE_CUSTOMIZER_DATA + customizerKey;
    byte[] serialData = m_env.getByteArray(key, null);
    if (serialData != null) {
      try {
        Object customizerData =
            SerializationUtility.createObjectSerializer().deserialize(serialData, null);
        return customizerData;
      } catch (Throwable t) {
        LOG.error("Failed reading custom table data for " + key + ": " + t);
        m_env.remove(key);
        return null;
      }
    } else {
      return null;
    }
  }

  /** store customizer data to persistent store */
  public void setTableCustomizerData(String customizerKey, Object customizerData) {
    String key = TABLE_CUSTOMIZER_DATA + customizerKey;
    if (customizerData != null) {
      try {
        byte[] data = SerializationUtility.createObjectSerializer().serialize(customizerData);
        m_env.putByteArray(key, data);
      } catch (Throwable t) {
        LOG.error("Failed storing custom table data for " + key, t);
        m_env.remove(key);
      }
    } else {
      m_env.remove(key);
    }
    //
    flush();
  }

  /** @return the key prefix used to store column data */
  public String getColumnKey(IColumn c) {
    String key = c.getColumnId();
    if (c.getTable() != null) {
      key = getTableKey(c.getTable()) + "#" + key;
    }
    return key;
  }

  public void setTableColumnPreferences(IColumn col) {
    setTableColumnPreferences(col, true);
  }

  public void setTableColumnPreferences(IColumn col, boolean flush) {
    String keySuffix = getColumnKey(col);
    String key = TABLE_COLUMN_UIINDEX + keySuffix;
    int viewIndex = col.getVisibleColumnIndexHint();
    boolean visible = col.isVisibleInternal();
    int width = col.getWidth();
    int sortIndex = col.getSortIndex();
    boolean sortUp = col.isSortAscending();
    boolean sortExplicit = col.isSortExplicit();
    //
    if (viewIndex >= 0) {
      m_env.put(key, "" + viewIndex);
    } else {
      m_env.remove(key);
    }
    //
    key = TABLE_COLUMN_VISIBLE + keySuffix;
    if (!visible) {
      m_env.put(key, "no");
    } else {
      m_env.put(key, "yes");
    }
    //
    key = getUserAgentPrefix() + TABLE_COLUMN_WIDTH + keySuffix;
    if (width >= 0) {
      m_env.put(key, "" + width);
    } else {
      m_env.remove(key);
    }
    //
    key = TABLE_COLUMN_SORT_INDEX + keySuffix;
    if (sortIndex >= 0) {
      m_env.put(key, "" + sortIndex);
    } else {
      m_env.put(key, "-1");
    }
    //
    key = TABLE_COLUMN_SORT_ASC + keySuffix;
    if (sortIndex >= 0 && sortUp) {
      m_env.put(key, "true");
    } else {
      m_env.put(key, "false");
    }
    //
    key = TABLE_COLUMN_SORT_EXPLICIT + keySuffix;
    if (sortExplicit) {
      m_env.put(key, "true");
    } else {
      m_env.put(key, "false");
    }

    if (flush) {
      flush();
    }
  }

  public void updateTableColumnOrder(List<IColumn<?>> columnList, int[] visibleColumnIndexHints) {
    if (columnList.size() != visibleColumnIndexHints.length) {
      throw new IllegalArgumentException(
          "columnList.size="
              + columnList.size()
              + " hints.length="
              + visibleColumnIndexHints.length);
    }
    for (int i = 0; i < visibleColumnIndexHints.length; i++) {
      IColumn<?> c = columnList.get(i);
      int viewIndex = visibleColumnIndexHints[i];
      String keySuffix = getColumnKey(c);
      String key = TABLE_COLUMN_UIINDEX + keySuffix;
      if (viewIndex >= 0) {
        m_env.put(key, "" + viewIndex);
      } else {
        m_env.remove(key);
      }
    }
    //
    flush();
  }

  public void removeTableColumnPreferences(IColumn col) {
    removeTableColumnPreferences(col, true, true, true, true);
  }

  public void removeTableColumnPreferences(
      IColumn col, boolean visibility, boolean order, boolean sorting, boolean widths) {
    removeTableColumnPreferences(col, visibility, order, sorting, widths, true);
  }

  private void removeTableColumnPreferences(
      IColumn col,
      boolean visibility,
      boolean order,
      boolean sorting,
      boolean widths,
      boolean flush) {
    if (col != null) {
      String keySuffix = getColumnKey(col);
      if (visibility) {
        m_env.remove(TABLE_COLUMN_VISIBLE + keySuffix);
      }
      if (order) {
        m_env.remove(TABLE_COLUMN_UIINDEX + keySuffix);
      }
      if (sorting) {
        m_env.remove(TABLE_COLUMN_SORT_INDEX + keySuffix);
        m_env.remove(TABLE_COLUMN_SORT_ASC + keySuffix);
        m_env.remove(TABLE_COLUMN_SORT_EXPLICIT + keySuffix);
      }
      if (widths) {
        m_env.remove(getUserAgentPrefix() + TABLE_COLUMN_WIDTH + keySuffix);
      }

      if (flush) {
        flush();
      }
    }
  }

  public void removeAllTableColumnPreferences(
      ITable table, boolean visibility, boolean order, boolean sorting, boolean widths) {
    if (table == null) {
      return;
    }

    for (IColumn<?> col : table.getColumns()) {
      removeTableColumnPreferences(col, visibility, order, sorting, widths, false);
    }

    flush();
  }

  public void setAllTableColumnPreferences(ITable table) {
    if (table == null) {
      return;
    }

    for (IColumn col : table.getColumns()) {
      if (col.isDisplayable()) {
        setTableColumnPreferences(col, false);
      }
    }

    flush();
  }

  /** @return true if there are any user preferences for this tables columns */
  public boolean hasTableColumnPreferences(ITable table) {
    if (table != null) {
      for (IColumn col : table.getColumns()) {
        String keySuffix = getColumnKey(col);
        if (m_env.get(TABLE_COLUMN_VISIBLE + keySuffix, null) != null) {
          return true;
        }
        if (m_env.get(TABLE_COLUMN_UIINDEX + keySuffix, null) != null) {
          return true;
        }
        if (m_env.get(TABLE_COLUMN_SORT_INDEX + keySuffix, null) != null) {
          return true;
        }
        if (m_env.get(TABLE_COLUMN_SORT_ASC + keySuffix, null) != null) {
          return true;
        }
        if (m_env.get(TABLE_COLUMN_SORT_EXPLICIT + keySuffix, null) != null) {
          return true;
        }
        if (m_env.get(TABLE_COLUMN_WIDTH + keySuffix, null) != null) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Since this property depends on the user agent it is saved separately for each combination of
   * {@link org.eclipse.scout.rt.shared.ui.IUiLayer IUiLayer} and {@link
   * org.eclipse.scout.rt.shared.ui.IUiDeviceType IUiDeviceType}.
   */
  public int getTableColumnWidth(IColumn col, int defaultWidth) {
    String keySuffix = getColumnKey(col);
    String baseKey = TABLE_COLUMN_WIDTH + keySuffix;
    String key = getUserAgentPrefix() + baseKey;

    String value = m_env.get(key, null);
    if (value == null) {
      // If no value has been found try to load with the base key. Done due to backward
      // compatibility.
      value = m_env.get(baseKey, null);
    }
    if (value != null) {
      try {
        return Integer.parseInt(value);
      } catch (Exception e) {
        LOG.warn("value=" + value, e);
      }
    }
    return defaultWidth;
  }

  public boolean getTableColumnVisible(IColumn col, boolean defaultValue) {
    String keySuffix = getColumnKey(col);
    String key = TABLE_COLUMN_VISIBLE + keySuffix;
    String value = m_env.get(key, null);
    if (value != null) {
      Boolean b = TypeCastUtility.castValue(value, Boolean.class);
      return b != null ? b.booleanValue() : defaultValue;
    }
    return defaultValue;
  }

  public int getTableColumnViewIndex(IColumn col, int defaultIndex) {
    String keySuffix = getColumnKey(col);
    String key = TABLE_COLUMN_UIINDEX + keySuffix;
    String value = m_env.get(key, null);
    if (value != null) {
      try {
        return Integer.parseInt(value);
      } catch (Exception e) {
        LOG.warn("value=" + value, e);
      }
    }
    return defaultIndex;
  }

  public int getTableColumnSortIndex(IColumn col, int defaultValue) {
    String keySuffix = getColumnKey(col);
    String key = TABLE_COLUMN_SORT_INDEX + keySuffix;
    String value = m_env.get(key, null);
    if (value != null) {
      Integer i = TypeCastUtility.castValue(value, Integer.class);
      return i.intValue();
    }
    return defaultValue;
  }

  public boolean getTableColumnSortAscending(IColumn col, boolean defaultValue) {
    String keySuffix = getColumnKey(col);
    String key = TABLE_COLUMN_SORT_ASC + keySuffix;
    String value = m_env.get(key, null);
    if (value != null) {
      Boolean b = TypeCastUtility.castValue(value, Boolean.class);
      return b != null ? b.booleanValue() : defaultValue;
    }
    return defaultValue;
  }

  public Boolean getTableColumnSortExplicit(IColumn col) {
    String keySuffix = getColumnKey(col);
    String key = TABLE_COLUMN_SORT_EXPLICIT + keySuffix;
    String value = m_env.get(key, null);
    if (value != null) {
      return TypeCastUtility.castValue(value, Boolean.class);
    }
    return null;
  }

  public void setApplicationWindowPreferences(BoundsSpec r, boolean maximized) {
    if (r != null) {
      String value = "" + r.x + "," + r.y + "," + r.width + "," + r.height;
      m_env.put(APPLICATION_WINDOW_BOUNDS, value);
    } else {
      m_env.remove(APPLICATION_WINDOW_BOUNDS);
    }
    //
    if (maximized) {
      m_env.put(APPLICATION_WINDOW_MAXIMIZED, "yes");
    } else {
      m_env.remove(APPLICATION_WINDOW_MAXIMIZED);
    }
    flush();
  }

  public boolean getApplicationWindowMaximized() {
    String key = APPLICATION_WINDOW_MAXIMIZED;
    String value = m_env.get(key, null);
    return "yes".equalsIgnoreCase(value);
  }

  public BoundsSpec getApplicationWindowBounds() {
    String key = APPLICATION_WINDOW_BOUNDS;
    String value = m_env.get(key, null);
    if (value != null) {
      try {
        StringTokenizer tok = new StringTokenizer(value, ",");
        BoundsSpec r =
            new BoundsSpec(
                new Integer(tok.nextToken()).intValue(),
                new Integer(tok.nextToken()).intValue(),
                new Integer(tok.nextToken()).intValue(),
                new Integer(tok.nextToken()).intValue());
        return r;
      } catch (Exception e) {
        LOG.warn("value=" + value, e);
      }
    }
    return null;
  }

  public void setCalendarPreferences(int displayMode, boolean displayCondensed) {
    m_env.put(CALENDAR_DISPLAY_MODE, "" + displayMode);
    m_env.put(CALENDAR_DISPLAY_CONDENSED, "" + displayCondensed);
    flush();
  }

  public int getCalendarDisplayMode(int defaultValue) {
    String key = CALENDAR_DISPLAY_MODE;
    String value = m_env.get(key, null);
    if (value != null) {
      try {
        return Integer.parseInt(value);
      } catch (Exception e) {
        LOG.warn("value=" + value, e);
      }
    }
    return defaultValue;
  }

  public boolean getCalendarDisplayCondensed(boolean defaultValue) {
    String key = CALENDAR_DISPLAY_CONDENSED;
    String value = m_env.get(key, null);
    if (value != null) {
      try {
        return TypeCastUtility.castValue(value, Boolean.class);
      } catch (Exception e) {
        LOG.warn("value=" + value, e);
      }
    }
    return defaultValue;
  }

  public int getPropertyInteger(String propName, int defaultValue, boolean setDefaultAsProperty) {
    String value = m_env.get(propName, null);
    if (value != null) {
      try {
        return TypeCastUtility.castValue(value, Integer.class);
      } catch (Exception e) {
        LOG.warn("value=" + value, e);
        if (setDefaultAsProperty) {
          setPropertyInteger(propName, defaultValue);
        }
      }
    } else {
      if (setDefaultAsProperty) {
        setPropertyInteger(propName, defaultValue);
      }
    }
    return defaultValue;
  }

  public void setPropertyInteger(String propName, int value) {
    m_env.put(propName, "" + value);
    flush();
  }

  public int[] getPropertyIntArray(String propName) {
    String strVal = m_env.get(propName, null);
    if (!StringUtility.hasText(strVal)) {
      return null;
    }

    String[] split = strVal.split(";");
    int[] val = new int[split.length];
    for (int i = 0; i < split.length; i++) {
      val[i] = Integer.parseInt(split[i]);
    }
    return val;
  }

  public void setPropertyIntArray(String propName, int[] value) {
    StringBuilder builder = new StringBuilder();
    if (value != null) {
      for (int i = 0; i < value.length; i++) {
        builder.append(value[i]);
        if (i != value.length - 1) {
          builder.append(";");
        }
      }
    }
    m_env.put(propName, builder.toString());
    flush();
  }

  public Double getPropertyDouble(String propName) {
    String value = m_env.get(propName, null);
    if (value != null) {
      try {
        return TypeCastUtility.castValue(value, Double.class);
      } catch (Exception e) {
        LOG.warn("value=" + value, e);
      }
    }
    return null;
  }

  public void setPropertyDouble(String propName, Double value) {
    m_env.put(propName, "" + value);
    flush();
  }

  public int[][] getDesktopColumnSplits(int rowCount, int colCount) {
    // the row x col matrix is stored as flat int array, row per row, column per column
    int[] a = getPropertyIntArray(DESKTOP_COLUMN_SPLITS);
    if (a != null && a.length == rowCount * colCount) {
      int[][] splits = new int[rowCount][colCount];
      int index = 0;
      for (int r = 0; r < rowCount; r++) {
        for (int c = 0; c < colCount; c++) {
          splits[r][c] = a[index];
          index++;
        }
      }
      return splits;
    }
    return null;
  }

  public void setDesktopColumnSplits(int[][] splits) {
    // the row x col matrix is stored as flat int array, row per row, column per column
    if (splits != null) {
      int rowCount = splits.length;
      int colCount = splits[0].length;
      int[] a = new int[rowCount * colCount];
      int index = 0;
      for (int r = 0; r < rowCount; r++) {
        for (int c = 0; c < colCount; c++) {
          a[index] = splits[r][c];
          index++;
        }
      }
      setPropertyIntArray(DESKTOP_COLUMN_SPLITS, a);
    }
  }

  protected void load() {
    if (m_session == null || ClientSessionThreadLocal.get() == m_session) {
      m_env = SERVICES.getService(IUserPreferencesStorageService.class).loadPreferences();
    } else {
      ClientAsyncJob job =
          new ClientAsyncJob("Load user preferences", m_session) {
            @Override
            protected void runVoid(IProgressMonitor monitor) throws Throwable {
              m_env = SERVICES.getService(IUserPreferencesStorageService.class).loadPreferences();
            }
          };
      job.schedule();
      try {
        job.join();
      } catch (InterruptedException e) {
        // nop
      }
    }
    if (m_env == null) {
      throw new IllegalStateException("Could not load preferences in client job");
    }
  }

  protected void flush() {
    try {
      m_env.flush();
    } catch (Throwable t) {
      t.printStackTrace();
    }
  }
}
public abstract class AbstractRwtScoutSvgComposite<T extends IFormField>
    extends RwtScoutFieldComposite<T> {
  private static final IScoutLogger LOG =
      ScoutLogManager.getLogger(AbstractRwtScoutSvgComposite.class);

  /* because the scroll-bar layout of IE is different: add an offset to ensure the full svg element can be shown without scrollbars */
  protected static final int SVG_ELEMENT_INNER_SPACE = 7;

  private static final String DOCUMENT_ENCODING = "UTF-8";

  private BrowserExtension m_browserExtension;

  private void setBrowserExtension(BrowserExtension browserExtension) {
    m_browserExtension = browserExtension;
  }

  protected BrowserExtension getBrowserExtension() {
    return m_browserExtension;
  }

  @Override
  protected void initializeUi(Composite parent) {
    super.initializeUi(parent);

    // create container for label and svg browser
    Composite container = getUiEnvironment().getFormToolkit().createComposite(parent);
    setUiContainer(container);

    // create label
    StatusLabelEx label =
        getUiEnvironment().getFormToolkit().createStatusLabel(container, getScoutObject());

    // create browser that shows the SVG
    Browser browser = getUiEnvironment().getFormToolkit().createBrowser(container, SWT.NO_SCROLL);
    browser.addDisposeListener(
        new DisposeListener() {
          private static final long serialVersionUID = 1L;

          @Override
          public void widgetDisposed(DisposeEvent e) {
            getBrowserExtension().detach();
          }
        });
    browser.addLocationListener(
        new LocationAdapter() {
          private static final long serialVersionUID = 1L;

          @Override
          public void changed(LocationEvent event) {
            locationChangedFromUi(event);
          }
        });
    browser.addControlListener(
        new ControlListener() {

          private static final long serialVersionUID = 1L;

          @Override
          public void controlResized(ControlEvent e) {
            updateSvgDocument();
          }

          @Override
          public void controlMoved(ControlEvent e) {
            updateSvgDocument();
          }
        });
    setBrowserExtension(new BrowserExtension(browser));
    getBrowserExtension().attach();

    setUiLabel(label);
    setUiField(browser);

    // layout
    getUiContainer().setLayout(new LogicalGridLayout(1, 0));
  }

  protected static String getSvgContentFromDocument(SVGDocument doc) throws ProcessingException {
    try {
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      DOMSource domSource = new DOMSource(doc.getRootElement());
      StreamResult streamResult = new StreamResult(out);
      Transformer t = TransformerFactory.newInstance().newTransformer();
      t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
      t.setOutputProperty(OutputKeys.ENCODING, DOCUMENT_ENCODING);
      t.transform(domSource, streamResult);
      out.close();
      return new String(out.toByteArray(), DOCUMENT_ENCODING);
    } catch (Exception e) {
      throw new ProcessingException("Writing SVG Failed", e);
    }
  }

  protected void updateSvgDocument() {
    getBrowserExtension().clearLocalHyperlinkCache();
    getBrowserExtension().clearResourceCache();

    SVGDocument doc = getSvgDocument();
    if (doc == null) {
      getUiField().setText("");
      return;
    }
    try {
      // set the dimensions of the svg element to the size of the browser field
      Rectangle browserBounds = getAbsoluteBrowserBounds();
      doc.getRootElement()
          .setAttribute(SVGConstants.SVG_HEIGHT_ATTRIBUTE, browserBounds.height + "px");
      doc.getRootElement()
          .setAttribute(SVGConstants.SVG_WIDTH_ATTRIBUTE, browserBounds.width + "px");

      // get the svg code as string and rewrite the local links
      String svgText =
          getBrowserExtension().adaptLocalHyperlinks(getSvgContentFromDocument(doc), 2);

      // bugfix for SVG fields to ensure all context menus are closed when the user clicks into the
      // svg field
      String contextMenuHideScript =
          "parent.parent.org.eclipse.rwt.MenuManager.getInstance().update(null, 'mousedown');";

      // bugfix so that the svg field inherits the color of the parent container. otherwise it is
      // always defined white.
      String backgroundColorInheritScript = null;
      if (getScoutObject().getBackgroundColor() == null) {
        backgroundColorInheritScript =
            "var iframes = parent.document.getElementsByTagName('iframe');"
                + "for(var i=0;i<iframes.length;i++) {"
                + "  var field=iframes[i].parentNode;"
                + "  var color=field.style.backgroundColor;"
                + "  if(color && color.toLowerCase() === 'rgb(255, 255, 255)') "
                + "    field.style.backgroundColor='';"
                + "}";
      } else {
        backgroundColorInheritScript = "";
      }

      // set the html content to the browser
      getUiField()
          .setText(
              "<html><body width=\"100%\" height=\"100%\" onload=\""
                  + backgroundColorInheritScript
                  + "\" onclick=\""
                  + contextMenuHideScript
                  + "\">"
                  + svgText
                  + "</body></html>");
    } catch (Exception e) {
      LOG.error("preparing svg browser content", e);
      getUiField().setText("");
    }
  }

  protected Rectangle getAbsoluteBrowserBounds() {
    Point pt = getUiField().getDisplay().map(getUiField(), null, new Point(0, 0));
    return new Rectangle(
        pt.x,
        pt.y,
        getUiField().getBounds().width - SVG_ELEMENT_INNER_SPACE,
        getUiField().getBounds().height);
  }

  @Override
  public Browser getUiField() {
    return (Browser) super.getUiField();
  }

  protected abstract SVGDocument getSvgDocument();

  protected abstract void locationChangedFromUi(LocationEvent event);

  @Override
  protected void setEnabledFromScout(boolean b) {
    // nop
  }
}
/**
 * This resolver intercepts webservice requests prior to being propagated to the port type in order
 * to bind the call to a {@link IServerSession} context.
 */
@SuppressWarnings("restriction")
public class ScoutInstanceResolver<T> extends AbstractMultiInstanceResolver<T> {

  private static final IScoutLogger LOG = ScoutLogManager.getLogger(ScoutInstanceResolver.class);

  private IServerSessionFactory m_sessionFactory;

  public ScoutInstanceResolver(Class<T> portTypeClass) {
    super(portTypeClass);
    if (portTypeClass == null) {
      throw new WebServiceException("No port type class configured in sun-jaxws.xml");
    }
  }

  @Override
  public void start(WSWebServiceContext context, WSEndpoint endpoint) {
    m_sessionFactory = createSessionFactory(clazz);
    super.start(context, endpoint);
  }

  @Override
  public T resolve(Packet packet) {
    return super
        .create(); // creates a new port type instance, injects the @{WebServiceContext} and invokes
    // the method annotated with @{link PostConstruct}
  }

  protected IServerSessionFactory createSessionFactory(Class<?> portTypeClass) {
    ScoutWebService annotation = portTypeClass.getAnnotation(ScoutWebService.class);
    if (annotation == null) {
      return null;
    }
    try {
      return annotation.sessionFactory().newInstance();
    } catch (Exception e) {
      LOG.error("Error occured while creating session factory.", e);
    }
    return null;
  }

  protected IServerSession getSession(MessageContext context) {
    if (m_sessionFactory == null) {
      return null;
    }
    // Prefer cached session over creating a new one.
    // However, the session is only considered if created by the same type of factory.
    // This is to ensure a proper session context which is what the user is expecting.
    IServerSession contextSession = ContextHelper.getContextSession(context);
    Class<? extends IServerSessionFactory> contextSessionFactory =
        ContextHelper.getContextSessionFactoryClass(context);
    if (contextSession == null
        || !CompareUtility.equals(m_sessionFactory.getClass(), contextSessionFactory)) {
      // create a new session
      return SessionHelper.createNewServerSession(m_sessionFactory);
    }
    // cached session
    return contextSession;
  }

  @Override
  public Invoker createInvoker() {
    return new P_Invoker();
  }

  protected class P_Invoker extends Invoker {

    protected WSWebServiceContext m_context;

    @Override
    public void start(final WSWebServiceContext context, final WSEndpoint endpoint) {
      m_context = context;
      ScoutInstanceResolver.this.start(context, endpoint);
    }

    @Override
    public void dispose() {
      m_context = null;
      ScoutInstanceResolver.this.dispose();
    }

    @Override
    public Object invoke(final Packet packet, final Method method, final Object... aobj)
        throws InvocationTargetException, IllegalAccessException {
      final T portType = ScoutInstanceResolver.this.resolve(packet);
      if (portType == null) {
        throw new WebServiceException("No port type found");
      }

      Subject subject = null;
      try {
        subject = Subject.getSubject(AccessController.getContext());
      } catch (Exception e) {
        LOG.error("Failed to get subject of calling access context", e);
      }
      if (subject == null) {
        throw new WebServiceException(
            "Webservice request was NOT dispatched due to security reasons: request must run on behalf of a subject context.");
      }
      IServerSession session = getSession(m_context.getMessageContext());
      if (session == null) {
        LOG.warn(
            "Webservice request is not run in a session context as no server session is configured.");
        return method.invoke(portType, aobj);
      }

      try {
        final ObjectHolder resultHolder = new ObjectHolder();
        final Holder<InvocationTargetException> invocationTargetExceptionHolder =
            new Holder<InvocationTargetException>(InvocationTargetException.class);
        final Holder<IllegalAccessException> illegalAccessExceptionHolder =
            new Holder<IllegalAccessException>(IllegalAccessException.class);
        final Holder<RuntimeException> runtimeExceptionHolder =
            new Holder<RuntimeException>(RuntimeException.class);
        // run server job
        final IServerJobFactory jobFactory =
            SERVICES.getService(IServerJobService.class).createJobFactory(session, subject);
        ServerJob serverJob =
            jobFactory.create(
                "Tx",
                new ITransactionRunnable() {

                  @Override
                  public IStatus run(IProgressMonitor monitor) throws ProcessingException {
                    try {
                      resultHolder.setValue(method.invoke(portType, aobj));
                    } catch (InvocationTargetException e) {
                      Throwable cause = e.getCause();
                      ThreadContext.getTransaction().addFailure(cause); // rollback transaction

                      if (cause instanceof RuntimeException) {
                        LOG.warn(
                            "Webservice processing exception occured. Please handle faults by respective checked SOAP faults.",
                            cause);
                        invocationTargetExceptionHolder.setValue(
                            new InvocationTargetException(
                                new WebServiceException("Internal Server Error")));
                      } else {
                        // business exception (SOAP faults are checked exceptions)
                        LOG.info("Webservice processing exception occured.", cause);
                        invocationTargetExceptionHolder.setValue(e);
                      }
                    } catch (IllegalAccessException e) {
                      ThreadContext.getTransaction().addFailure(e); // rollback transaction
                      LOG.error(
                          "Illegal access exception occured while dispatching webservice request. This might be caused because of Java security settings.",
                          e);
                      illegalAccessExceptionHolder.setValue(e);
                    } catch (RuntimeException e) {
                      ThreadContext.getTransaction().addFailure(e); // rollback transaction
                      LOG.error(
                          "Unexpected error occured while dispatching webservice request.", e);
                      runtimeExceptionHolder.setValue(e);
                    }

                    return Status.OK_STATUS;
                  }
                });
        serverJob.setSystem(true);
        serverJob.runNow(new NullProgressMonitor());
        if (invocationTargetExceptionHolder.getValue() != null) {
          throw invocationTargetExceptionHolder.getValue();
        }
        if (illegalAccessExceptionHolder.getValue() != null) {
          throw illegalAccessExceptionHolder.getValue();
        }
        if (runtimeExceptionHolder.getValue() != null) {
          throw runtimeExceptionHolder.getValue();
        }
        return resultHolder.getValue();
      } finally {
        postInvoke(packet, portType);
      }
    }
  }
}
public abstract class AbstractKeyboardNavigationSupport {
  private static final IScoutLogger LOG =
      ScoutLogManager.getLogger(AbstractKeyboardNavigationSupport.class);
  private final long m_delay;
  private long m_timeoutTimestamp;
  private String m_filterText = "";
  private Object navigationLock = new Object();
  private P_NavigationJob m_navigationJob;

  public AbstractKeyboardNavigationSupport() {
    this(1000L);
  }

  public AbstractKeyboardNavigationSupport(long delay) {
    m_delay = delay;
    m_navigationJob = new P_NavigationJob();
  }

  public void addChar(char c) {
    synchronized (navigationLock) {
      if (Character.isWhitespace(c) || Character.isLetterOrDigit(c)) {
        if (System.currentTimeMillis() > m_timeoutTimestamp) {
          m_filterText = "";
        }
        String newText = "" + Character.toLowerCase(c);
        m_filterText += newText;
        if (m_navigationJob != null) {
          m_navigationJob.cancel();
        } else {
          m_navigationJob = new P_NavigationJob();
        }
        m_navigationJob.schedule(250L);
        m_timeoutTimestamp = System.currentTimeMillis() + m_delay;
      }
    }
  }

  abstract void handleSearchPattern(String regex);

  private class P_NavigationJob extends Job {

    public P_NavigationJob() {
      super("");
      setSystem(true);
    }

    @Override
    protected IStatus run(IProgressMonitor monitor) {
      String pattern;
      synchronized (navigationLock) {
        if (monitor.isCanceled() || StringUtility.isNullOrEmpty(m_filterText)) {
          return Status.CANCEL_STATUS;
        }
        pattern = StringUtility.toRegExPattern(m_filterText.toLowerCase());
        pattern = pattern + ".*";
      }
      // this call must be outside lock!
      handleSearchPattern(pattern);
      return Status.OK_STATUS;
    }
  } // end class P_NavigationJob
}
@ClassId("6a093505-c2b1-4df2-84d6-e799f91e6e7c")
public abstract class AbstractGroupBox extends AbstractCompositeField implements IGroupBox {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(AbstractGroupBox.class);

  private IGroupBoxUIFacade m_uiFacade;
  private boolean m_mainBoxFlag = false;
  private int m_gridColumnCountHint;
  private boolean m_scrollable;
  private List<IFormField> m_controlFields;
  private List<IGroupBox> m_groupBoxes;
  private List<IButton> m_customButtons;
  private List<IButton> m_systemButtons;
  private IGroupBoxBodyGrid m_bodyGrid;
  private GroupBoxProcessButtonGrid m_customProcessButtonGrid;
  private GroupBoxProcessButtonGrid m_systemProcessButtonGrid;

  public AbstractGroupBox() {
    this(true);
  }

  public AbstractGroupBox(boolean callInitializer) {
    super(callInitializer);
  }

  /** {@inheritDoc} Default for group boxes is true. */
  @Override
  protected boolean getConfiguredGridUseUiHeight() {
    return true;
  }

  /**
   * Configures the number of columns used in this group box.<br>
   * A typical {@link IFormField} inside a group box spans one column. This behavior can be changed
   * by setting {@link AbstractFormField#getConfiguredGridW()}.
   *
   * <p>Subclasses can override this method. Default is -1 which typically means 2 columns.
   *
   * @return the number of columns used in this group box
   */
  @ConfigProperty(ConfigProperty.INTEGER)
  @Order(200)
  protected int getConfiguredGridColumnCount() {
    return -1;
  }

  /** @return the body grid responsible to set {@link GridData} to the fields in this group box. */
  @ConfigProperty(ConfigProperty.GROUP_BOX_BODY_GRID)
  @Order(210)
  protected Class<? extends IGroupBoxBodyGrid> getConfiguredBodyGrid() {
    return VerticalSmartGroupBoxBodyGrid.class;
  }

  /**
   * Configures the border visibility for this group box. <br>
   * If the property is set to true, a border will be displayed around the group box. The style of
   * the border is configured by {@link #getConfiguredBorderDecoration()}.<br>
   * If the property is set to false, no border will be displayed and the margin reserved for the
   * border will be removed.
   *
   * <p><b>Hint:</b> Keep in mind that setting the border to invisible also removes the margin which
   * could lead to a misalignment of the fields if several group boxes are used on a form. In order
   * to preserve the correct alignment consider using {@link #getConfiguredBorderDecoration()} with
   * {@link IGroupBox#BORDER_DECORATION_EMPTY} and {@link #getConfiguredBorderVisible()} with {@code
   * true} instead.
   *
   * <p>Subclasses can override this method. Default is {@code true}.
   *
   * @return {@code true} if the border is visible, {@code false} otherwise.
   */
  @ConfigProperty(ConfigProperty.BOOLEAN)
  @Order(230)
  protected boolean getConfiguredBorderVisible() {
    return true;
  }

  /**
   * Configures whether this group box should be expandable or not.<br>
   * This property depends on the border decoration which can be configured by {@link
   * #getConfiguredBorderDecoration()}. It typically only has an effect if the border decoration is
   * set to {@link IGroupBox#BORDER_DECORATION_SECTION} or {@link IGroupBox#BORDER_DECORATION_AUTO}.
   *
   * <p>Subclasses can override this method. Default is {@code false}.
   *
   * @return {@code true} if the group box should be expandable, {@code false} otherwise.
   */
  @ConfigProperty(ConfigProperty.BOOLEAN)
  @Order(231)
  protected boolean getConfiguredExpandable() {
    return false;
  }

  /**
   * Configures whether this group box is initially expanded. <br>
   * This property only has an effect if the group box is expandable which can be configured by
   * {@link #getConfiguredExpandable()}.
   *
   * <p>Subclasses can override this method. Default is {@code true}.
   *
   * @return {@code true} if the group box should be initially expanded, {@code false} otherwise.
   */
  @ConfigProperty(ConfigProperty.BOOLEAN)
  @Order(232)
  protected boolean getConfiguredExpanded() {
    return true;
  }

  /**
   * Configures the border decoration for this group box. See {@code IGroupBox#BORDER_DECORATION_*}
   * constants for valid values.<br>
   * This property only has an effect if the border is visible which can be configured by {@link
   * #getConfiguredBorderVisible()}.
   *
   * <p>Subclasses can override this method. Default is {@link IGroupBox#BORDER_DECORATION_AUTO}.
   *
   * @return the border decoration of the group box
   */
  @ConfigProperty(ConfigProperty.BORDER_DECORATION)
  @Order(233)
  protected String getConfiguredBorderDecoration() {
    return BORDER_DECORATION_AUTO;
  }

  /**
   * Configures the background image for this group box.
   *
   * <p>Subclasses can override this method. Default is {@code null}.
   *
   * @return the ID (name) of the image
   * @see IIconProviderService
   */
  @ConfigProperty(ConfigProperty.ICON_ID)
  @Order(240)
  protected String getConfiguredBackgroundImageName() {
    return null;
  }

  /**
   * Configures the horizontal alignment of the background image.<br>
   * This property only has an effect if the group box has a background image which can be
   * configured by {@link #getConfiguredBackgroundImageName()}
   *
   * <p>Subclasses can override this method. Default alignment is center.
   *
   * @return -1 for left, 0 for center and 1 for right alignment
   */
  @ConfigProperty(ConfigProperty.HORIZONTAL_ALIGNMENT)
  @Order(250)
  protected int getConfiguredBackgroundImageHorizontalAlignment() {
    return 0;
  }

  /**
   * Configures the vertical alignment of the background image.<br>
   * This property only has an effect if the group box has a background image which can be
   * configured by {@link #getConfiguredBackgroundImageName()}
   *
   * <p>Subclasses can override this method. Default alignment is center.
   *
   * @return -1 for top, 0 for center and 1 for bottom alignment
   */
  @ConfigProperty(ConfigProperty.VERTICAL_ALIGNMENT)
  @Order(260)
  protected int getConfiguredBackgroundImageVerticalAlignment() {
    return 0;
  }

  /**
   * Configures whether this group box should be scrollable.</br> If the property is set to true, a
   * vertical scrollbar will appear if the content is too large to be displayed.
   *
   * <p>Subclasses can override this method. Default is false.
   *
   * @return {@code true} if the group box should be scrollable, {@code false} otherwise.
   */
  @ConfigProperty(ConfigProperty.BOOLEAN)
  @Order(270)
  protected boolean getConfiguredScrollable() {
    return false;
  }

  /**
   * Configures the column span of this group box.<br>
   * The value defined by this property refers to the number of columns defined by the container of
   * this group box. <br>
   * The column count of the container, which actually is the parent group box, can be configured by
   * {@link #getConfiguredGridColumnCount()} (you need to configure that in the parent group box).
   *
   * <p><b>Example:</b> If the column count of the container is set to 3 and a column span of this
   * group box is set to 2 it means 2/3 of the container width is used for this group box.
   *
   * <p>Subclasses can override this method. Default is {@link IFormField#FULL_WIDTH} which means it
   * spans every column of the container.
   *
   * @return the number of columns to span
   */
  @Override
  protected int getConfiguredGridW() {
    return FULL_WIDTH;
  }

  @Override
  protected void initConfig() {
    m_uiFacade = new P_UIFacade();
    Class<? extends IGroupBoxBodyGrid> bodyGridClazz = getConfiguredBodyGrid();
    if (bodyGridClazz != null) {
      IGroupBoxBodyGrid bodyGrid;
      try {
        bodyGrid = bodyGridClazz.newInstance();
        setBodyGrid(bodyGrid);
      } catch (Exception e) {
        SERVICES
            .getService(IExceptionHandlerService.class)
            .handleException(
                new ProcessingException(
                    "error creating instance of class '" + bodyGridClazz.getName() + "'.", e));
      }
    }
    m_customProcessButtonGrid = new GroupBoxProcessButtonGrid(this, true, false);
    m_systemProcessButtonGrid = new GroupBoxProcessButtonGrid(this, false, true);
    super.initConfig();
    categorizeFields();

    setExpandable(getConfiguredExpandable());
    setExpanded(getConfiguredExpanded());
    setBorderVisible(getConfiguredBorderVisible());
    setBorderDecoration(getConfiguredBorderDecoration());
    setGridColumnCountHint(getConfiguredGridColumnCount());
    setBackgroundImageName(getConfiguredBackgroundImageName());
    setBackgroundImageHorizontalAlignment(getConfiguredBackgroundImageHorizontalAlignment());
    setBackgroundImageVerticalAlignment(getConfiguredBackgroundImageVerticalAlignment());
    setScrollable(getConfiguredScrollable());
  }

  private void categorizeFields() {
    // categorize items
    List<IFormField> controlList = new ArrayList<IFormField>();
    List<IGroupBox> groupList = new ArrayList<IGroupBox>();
    List<IButton> customButtonList = new ArrayList<IButton>();
    List<IButton> systemButtonList = new ArrayList<IButton>();
    for (IFormField field : getFields()) {
      if (field instanceof IGroupBox) {
        groupList.add((IGroupBox) field);
        controlList.add(field);
      } else if (field instanceof IButton) {
        IButton b = (IButton) field;
        if (b.isProcessButton()) {
          if (b.getSystemType() != IButton.SYSTEM_TYPE_NONE) {
            systemButtonList.add((IButton) field);
          } else {
            customButtonList.add((IButton) field);
          }
        } else {
          controlList.add(field);
        }
      } else {
        controlList.add(field);
      }
    }
    m_controlFields = controlList;
    m_groupBoxes = groupList;
    m_customButtons = customButtonList;
    m_systemButtons = systemButtonList;
  }

  private void ensureCategorized() {
    if (m_controlFields == null
        || m_groupBoxes == null
        || m_customButtons == null
        || m_systemButtons == null) {
      categorizeFields();
    }
  }

  private void clearCategorization() {
    m_controlFields = null;
    m_groupBoxes = null;
    m_customButtons = null;
    m_systemButtons = null;
  }

  @Override
  public List<IKeyStroke> getContributedKeyStrokes() {
    List<IKeyStroke> list = new ArrayList<IKeyStroke>(2);
    if (isMainBox() && (getForm() != null && getForm().getOuterForm() == null)) {
      // add default escape and enter key stroke only if no similar key stroke
      // is defined on the mainbox
      boolean hasEnter = false;
      boolean hasEscape = false;
      for (IKeyStroke ks : getLocalKeyStrokes()) {
        if ("enter".equalsIgnoreCase(ks.getKeyStroke())) {
          hasEnter = true;
        }
        if ("escape".equalsIgnoreCase(ks.getKeyStroke())) {
          hasEscape = true;
        }
      }
      if (!hasEnter) {
        try {
          DefaultFormEnterKeyStroke enterKeyStroke = new DefaultFormEnterKeyStroke(getForm());
          enterKeyStroke.initAction();
          list.add(enterKeyStroke);
        } catch (ProcessingException e) {
          LOG.error("could not initialize enter key stroke.", e);
        }
      }
      if (!hasEscape) {
        try {
          DefaultFormEscapeKeyStroke escKeyStroke = new DefaultFormEscapeKeyStroke(getForm());
          escKeyStroke.initAction();
          list.add(escKeyStroke);
        } catch (ProcessingException e) {
          LOG.error("could not initialize esc key stroke.", e);
        }
      }
    }
    return list;
  }

  @Override
  public void rebuildFieldGrid() {
    m_bodyGrid.validate(this);
    m_customProcessButtonGrid.validate();
    m_systemProcessButtonGrid.validate();
    if (isInitialized()) {
      if (getForm() != null) {
        getForm().structureChanged(this);
      }
    }
  }

  @Override
  public boolean isMainBox() {
    return m_mainBoxFlag;
  }

  @Override
  public void setMainBox(boolean b) {
    m_mainBoxFlag = b;
  }

  @Override
  public int getGroupBoxIndex(IGroupBox groupBox) {
    return m_groupBoxes.indexOf(groupBox);
  }

  @Override
  public int getCustomProcessButtonCount() {
    return m_customButtons.size();
  }

  @Override
  public int getGroupBoxCount() {
    return m_groupBoxes.size();
  }

  @Override
  public int getSystemProcessButtonCount() {
    return m_systemButtons.size();
  }

  @Override
  public List<IGroupBox> getGroupBoxes() {
    ensureCategorized();
    return CollectionUtility.arrayList(m_groupBoxes);
  }

  @Override
  public List<IFormField> getControlFields() {
    ensureCategorized();
    return CollectionUtility.arrayList(m_controlFields);
  }

  @Override
  public List<IButton> getCustomProcessButtons() {
    ensureCategorized();
    return CollectionUtility.arrayList(m_customButtons);
  }

  @Override
  public List<IButton> getSystemProcessButtons() {
    ensureCategorized();
    return CollectionUtility.arrayList(m_systemButtons);
  }

  public void setBodyGrid(IGroupBoxBodyGrid bodyGrid) {
    m_bodyGrid = bodyGrid;
  }

  @Override
  public void addField(IFormField f) {
    super.addField(f);
    clearCategorization();
  }

  @Override
  public void removeField(IFormField f) {
    super.removeField(f);
    clearCategorization();
  }

  @Override
  public IGroupBoxBodyGrid getBodyGrid() {
    return m_bodyGrid;
  }

  @Override
  public final int getGridColumnCount() {
    return m_bodyGrid.getGridColumnCount();
  }

  @Override
  public final int getGridRowCount() {
    return m_bodyGrid.getGridRowCount();
  }

  @Override
  public void setGridColumnCountHint(int c) {
    m_gridColumnCountHint = c;
    if (isInitialized()) {
      rebuildFieldGrid();
    }
  }

  @Override
  public int getGridColumnCountHint() {
    return m_gridColumnCountHint;
  }

  @Override
  public boolean isScrollable() {
    return m_scrollable;
  }

  @Override
  public void setScrollable(boolean scrollable) {
    if (m_scrollable != scrollable) {
      m_scrollable = scrollable;
      if (m_scrollable) {
        // force weighty to be > 0
        GridData gd = getGridDataHints();
        if (gd.weightY <= 0) {
          gd.weightY = 1;
          setGridDataHints(gd);
        }
      }
    }
  }

  // box is only visible when it has at least one visible item
  @Override
  protected void handleFieldVisibilityChanged() {
    super.handleFieldVisibilityChanged();
    if (isInitialized()) {
      rebuildFieldGrid();
    }
  }

  @Override
  public boolean isBorderVisible() {
    return propertySupport.getPropertyBool(PROP_BORDER_VISIBLE);
  }

  @Override
  public void setBorderVisible(boolean b) {
    propertySupport.setPropertyBool(PROP_BORDER_VISIBLE, b);
  }

  @Override
  public String getBorderDecoration() {
    return propertySupport.getPropertyString(PROP_BORDER_DECORATION);
  }

  @Override
  public void setBorderDecoration(String s) {
    propertySupport.setPropertyString(PROP_BORDER_DECORATION, s);
  }

  @Override
  public boolean isExpandable() {
    return propertySupport.getPropertyBool(PROP_EXPANDABLE);
  }

  @Override
  public void setExpandable(boolean b) {
    propertySupport.setPropertyBool(PROP_EXPANDABLE, b);
  }

  @Override
  public boolean isExpanded() {
    return propertySupport.getPropertyBool(PROP_EXPANDED);
  }

  @Override
  public void setExpanded(boolean b) {
    propertySupport.setPropertyBool(PROP_EXPANDED, b);
  }

  @Override
  public IGroupBoxUIFacade getUIFacade() {
    return m_uiFacade;
  }

  @Override
  public void setBackgroundImageName(String imageName) {
    propertySupport.setPropertyString(PROP_BACKGROUND_IMAGE_NAME, imageName);
  }

  @Override
  public String getBackgroundImageName() {
    return propertySupport.getPropertyString(PROP_BACKGROUND_IMAGE_NAME);
  }

  /** @since Build 178 */
  @Override
  public void setBackgroundImageVerticalAlignment(int a) {
    propertySupport.setPropertyInt(PROP_BACKGROUND_IMAGE_VERTICAL_ALIGNMENT, a);
  }

  /** @since Build 178 */
  @Override
  public int getBackgroundImageVerticalAlignment() {
    return propertySupport.getPropertyInt(PROP_BACKGROUND_IMAGE_VERTICAL_ALIGNMENT);
  }

  /** @since Build 178 */
  @Override
  public void setBackgroundImageHorizontalAlignment(int a) {
    propertySupport.setPropertyInt(PROP_BACKGROUND_IMAGE_HORIZONTAL_ALIGNMENT, a);
  }

  /** @since Build 178 */
  @Override
  public int getBackgroundImageHorizontalAlignment() {
    return propertySupport.getPropertyInt(PROP_BACKGROUND_IMAGE_HORIZONTAL_ALIGNMENT);
  }

  private class P_UIFacade implements IGroupBoxUIFacade {
    @Override
    public void setExpandedFromUI(boolean expanded) {
      setExpanded(expanded);
    }
  }

  protected static class LocalGroupBoxExtension<OWNER extends AbstractGroupBox>
      extends LocalCompositeFieldExtension<OWNER> implements IGroupBoxExtension<OWNER> {

    public LocalGroupBoxExtension(OWNER owner) {
      super(owner);
    }
  }

  @Override
  protected IGroupBoxExtension<? extends AbstractGroupBox> createLocalExtension() {
    return new LocalGroupBoxExtension<AbstractGroupBox>(this);
  }
}
public class RwtScoutBrowserField extends RwtScoutValueFieldComposite<IBrowserField>
    implements IRwtScoutBrowserField {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(RwtScoutBrowserField.class);

  private String m_currentLocation;
  private BrowserExtension m_browserExtension;

  public RwtScoutBrowserField() {}

  @Override
  protected void initializeUi(Composite parent) {
    Composite container = getUiEnvironment().getFormToolkit().createComposite(parent);
    StatusLabelEx label =
        getUiEnvironment().getFormToolkit().createStatusLabel(container, getScoutObject());

    Browser browser = getUiEnvironment().getFormToolkit().createBrowser(container, SWT.NONE);
    m_browserExtension = new BrowserExtension(browser);
    m_browserExtension.attach();
    browser.addDisposeListener(
        new DisposeListener() {
          private static final long serialVersionUID = 1L;

          @Override
          public void widgetDisposed(DisposeEvent e) {
            m_browserExtension.detach();
          }
        });
    browser.addLocationListener(
        new LocationAdapter() {
          private static final long serialVersionUID = 1L;

          @Override
          public void changing(LocationEvent event) {
            event.doit = fireBeforeLocationChangedFromUi(event.location);
          }

          @Override
          public void changed(LocationEvent event) {
            fireAfterLocationChangedFromUi(event.location);
          }
        });
    //
    setUiContainer(container);
    setUiLabel(label);
    setUiField(browser);
    // layout
    getUiContainer().setLayout(new LogicalGridLayout(1, 0));
  }

  @Override
  public Browser getUiField() {
    return (Browser) super.getUiField();
  }

  @Override
  protected void handleScoutPropertyChange(String name, Object newValue) {
    super.handleScoutPropertyChange(name, newValue);
    if (IBrowserField.PROP_LOCATION.equals(name)) {
      setLocationFromScout();
    }
  }

  @Override
  protected void setValueFromScout() {
    setLocationFromScout();
  }

  protected void setLocationFromScout() {
    m_browserExtension.clearResourceCache();
    m_browserExtension.clearLocalHyperlinkCache();
    String location = getScoutObject().getLocation();
    RemoteFile r = getScoutObject().getValue();
    if (location == null && r != null && r.exists()) {
      try {
        if (r.getName().matches(".*\\.(zip|jar)")) {
          location = registerResourcesInZip(r);
        } else {
          String content = IOUtility.getContent(r.getDecompressedReader());
          content = m_browserExtension.adaptLocalHyperlinks(content, 1);
          location =
              m_browserExtension.addResource(
                  r.getName(), new ByteArrayInputStream(content.getBytes("UTF-8")));
        }
        // Prevent caching by making the request unique
        if (location != null) {
          location += "?nocache=" + System.currentTimeMillis();
        }
      } catch (Throwable t) {
        LOG.error("preparing html content for " + r, t);
      }
    }
    m_currentLocation = location;
    if (m_currentLocation != null) {
      getUiField().setUrl(m_currentLocation);
    } else {
      getUiField().setText("");
    }
  }

  private String registerResourcesInZip(RemoteFile zipFile)
      throws ProcessingException, IOException, UnsupportedEncodingException, FileNotFoundException {
    String location = null;
    File tempDir = IOUtility.createTempDirectory("browser");
    try {
      zipFile.writeZipContentToDirectory(tempDir);
      String simpleName = zipFile.getName().replaceAll("\\.(zip|jar)", ".htm");
      // rewrite local urls and register resource
      int prefixLen = tempDir.getAbsolutePath().length() + 1;
      for (File f : IOUtility.listFilesInSubtree(tempDir, null)) {
        if (f.isFile()) {
          String path = f.getAbsolutePath().substring(prefixLen);
          if (path.toLowerCase().matches(".*\\.(htm|html)")) {
            String content =
                IOUtility.getContent(new InputStreamReader(new FileInputStream(f), "UTF-8"));
            content = m_browserExtension.adaptLocalHyperlinks(content, 1);
            if (location == null && path.startsWith(simpleName)) {
              // this is the index.html
              location =
                  m_browserExtension.addResource(
                      simpleName, new ByteArrayInputStream(content.getBytes("UTF-8")));
            } else {
              m_browserExtension.addResource(
                  path, new ByteArrayInputStream(content.getBytes("UTF-8")));
            }
          } else if (path.toLowerCase().matches(".*\\.(svg)")) {
            String content = IOUtility.getContent(new InputStreamReader(new FileInputStream(f)));
            content = m_browserExtension.adaptLocalHyperlinks(content, 1);
            m_browserExtension.addResource(
                path, new ByteArrayInputStream(content.getBytes("UTF-8")));
          } else {
            m_browserExtension.addResource(path, new FileInputStream(f));
          }
        }
      }
    } finally {
      if (tempDir != null) {
        IOUtility.deleteDirectory(tempDir);
      }
    }
    return location;
  }

  @Override
  protected void setEnabledFromScout(boolean b) {
    // nop
  }

  protected boolean fireBeforeLocationChangedFromUi(final String location) {
    final AtomicReference<Boolean> accept = new AtomicReference<Boolean>();
    synchronized (accept) {
      // notify Scout
      Runnable t =
          new Runnable() {
            @Override
            public void run() {
              synchronized (accept) {
                accept.set(
                    getScoutObject().getUIFacade().fireBeforeLocationChangedFromUI(location));
                accept.notifyAll();
              }
            }
          };
      getUiEnvironment().invokeScoutLater(t, 0);
      // end notify
      // wait at most 10 seconds
      try {
        accept.wait(10000L);
      } catch (InterruptedException e) {
        // nop
      }
    }
    return accept.get() != null ? accept.get().booleanValue() : false;
  }

  protected void fireAfterLocationChangedFromUi(final String location) {
    // notify Scout
    Runnable t =
        new Runnable() {
          @Override
          public void run() {
            getScoutObject().getUIFacade().fireAfterLocationChangedFromUI(location);
          }
        };
    getUiEnvironment().invokeScoutLater(t, 0);
    // end notify
  }
}
/**
 * Sql SELECT statement for getting {@link LookupRow}s.<br>
 *
 * <p>The expected columns are
 *
 * <ul>
 *   <li>Object key
 *   <li>String text
 *   <li>String iconId
 *   <li>String tooltip
 *   <li>String background color
 *   <li>String foreground color
 *   <li>String font
 *   <li>Boolean enabled
 *   <li>Object parentKey used in hierarchical structures to point to the parents primary key
 *   <li>{@link TriState} active (0,1,null,...) see {@link TriState#parse(Object)}
 * </ul>
 *
 * <p>Valid bind names are: Object key, String text, String all, Object rec, {@link TriState} active
 * <br>
 * Valid xml tags are: &lt;key&gt;, &lt;text&gt;, &lt;all&gt;, &lt;rec&gt;
 */
public abstract class AbstractSqlLookupService<T> extends AbstractLookupService<T> {

  private static final Pattern REFUSING_ALL_TAGS_REGEX =
      Pattern.compile("<all>\\s*and\\s*([0-9]+)\\s*=\\s*([0-9]+)\\s*</all>", Pattern.DOTALL);

  private static final IScoutLogger LOG = ScoutLogManager.getLogger(AbstractSqlLookupService.class);

  public AbstractSqlLookupService() {}

  /** Sql SELECT statement */
  @ConfigProperty(ConfigProperty.SQL)
  @Order(10)
  protected String getConfiguredSqlSelect() {
    return null;
  }

  /** sort column 0-based. -1 if sort is done in sql. */
  @ConfigProperty(ConfigProperty.INTEGER)
  @Order(20)
  protected int getConfiguredSortColumn() {
    return 1;
  }

  /** This method is called on server side to load lookup rows. */
  @ConfigOperation
  @Order(10)
  protected List<ILookupRow<T>> execLoadLookupRows(
      String originalSql, String preprocessedSql, ILookupCall<T> call) throws ProcessingException {
    Object[][] data = SQL.selectLimited(preprocessedSql, call.getMaxRowCount(), call);
    if (getConfiguredSortColumn() >= 0) {
      sortData(data, getConfiguredSortColumn());
    }
    try {
      Class<?> genericsParameterClass = Object.class;
      try {
        genericsParameterClass =
            TypeCastUtility.getGenericsParameterClass(getClass(), ILookupService.class);
      } catch (IllegalArgumentException e) {
        LOG.warn(
            "Unable to calculate type parameters for lookup service '"
                + getClass().getName()
                + "'. No key type validation will be performed.");
      }
      return createLookupRowArray(data, call, genericsParameterClass);
    } catch (IllegalArgumentException e) {
      throw new ProcessingException(
          "Unable to load lookup rows for lookup service '" + getClass().getName() + "'.", e);
    }
  }

  @Override
  public List<ILookupRow<T>> getDataByKey(ILookupCall<T> call) throws ProcessingException {
    String sql = getConfiguredSqlSelect();
    return execLoadLookupRows(sql, filterSqlByKey(sql), call);
  }

  @Override
  public List<ILookupRow<T>> getDataByText(ILookupCall<T> call) throws ProcessingException {
    // change wildcards * in text to db specific wildcards
    if (call.getText() != null) {
      String s = call.getText();
      String sqlWildcard = SERVICES.getService(ISqlService.class).getSqlStyle().getLikeWildcard();
      call.setText(s.replaceAll("[*]", sqlWildcard));
    }
    String sql = getConfiguredSqlSelect();
    return execLoadLookupRows(sql, filterSqlByText(sql), call);
  }

  @Override
  public List<ILookupRow<T>> getDataByAll(ILookupCall<T> call) throws ProcessingException {
    String sql = getConfiguredSqlSelect();
    if (containsRefusingAllTag(sql)) {
      throw new VetoException(ScoutTexts.get("SearchTextIsTooGeneral"));
    }
    List<ILookupRow<T>> rows = execLoadLookupRows(sql, filterSqlByAll(sql), call);
    return rows;
  }

  @Override
  public List<ILookupRow<T>> getDataByRec(ILookupCall<T> call) throws ProcessingException {
    String sql = getConfiguredSqlSelect();
    return execLoadLookupRows(sql, filterSqlByRec(sql), call);
  }

  /**
   * Process xml tags.<br>
   * Keep content of "key" tag.<br>
   * Remove text,all,rec tags.
   */
  protected String filterSqlByKey(String sqlSelect) {
    return StringUtility.removeTagBounds(
        StringUtility.removeTags(sqlSelect, new String[] {"text", "all", "rec"}), "key");
  }

  /**
   * Process xml tags.<br>
   * Keep content of "text" tag.<br>
   * Remove key,all,rec tags.
   */
  protected String filterSqlByText(String sqlSelect) {
    return StringUtility.removeTagBounds(
        StringUtility.removeTags(sqlSelect, new String[] {"key", "all", "rec"}), "text");
  }

  /**
   * Process xml tags.<br>
   * Keep content of "all" tag.<br>
   * Remove key,text,rec tags.
   */
  protected String filterSqlByAll(String sqlSelect) {
    return StringUtility.removeTagBounds(
        StringUtility.removeTags(sqlSelect, new String[] {"key", "text", "rec"}), "all");
  }

  protected static boolean containsRefusingAllTag(String sqlSelect) {
    Matcher m = REFUSING_ALL_TAGS_REGEX.matcher(sqlSelect.toLowerCase());
    if (m.find()) {
      if (!m.group(1).equals(m.group(2))) {
        return true;
      }
    }
    return false;
  }

  /**
   * Process xml tags.<br>
   * Keep content of "rec" tag.<br>
   * Remove key,text,all tags.
   */
  protected String filterSqlByRec(String sqlSelect) {
    return StringUtility.removeTagBounds(
        StringUtility.removeTags(sqlSelect, new String[] {"key", "text", "all"}), "rec");
  }
}
/**
 * {@link Permissions} store per userId without any notifications.
 *
 * <p>Maintains a map of one {@link Permissions} object per userId (derived from their Subject, see
 * {@link IAccessControlService#getUserIdOfCurrentSubject()}).
 *
 * <p>The userId is case insensitive, case does not matter.
 *
 * @since 4.3.0 (Mars-M5)
 */
public class AccessControlStore {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(AccessControlStore.class);

  /** the internal store, the {@link String} used as key is always lower case */
  private TTLCache<String /* userId */, Permissions> m_store;

  private Object m_storeLock;

  public AccessControlStore() {
    m_storeLock = new Object();
    m_store = new TTLCache<String, Permissions>(3600000L);
  }

  /** @return the permission collection that is associated with the current subject */
  public Permissions getPermissionsOfCurrentSubject() {
    String userId = SERVICES.getService(IAccessControlService.class).getUserIdOfCurrentSubject();
    if (userId == null) {
      return null;
    }
    return getPermissions(userId);
  }

  /**
   * sets permission collection that is associated with the current subject
   *
   * @param p permission collection
   */
  public void setPermissionsOfCurrentSubject(Permissions p) {
    String userId = SERVICES.getService(IAccessControlService.class).getUserIdOfCurrentSubject();
    if (userId == null) {
      throw new SecurityException("userId is null");
    }
    setPermissions(userId, p);
  }

  /**
   * @param userId of Subject
   * @return the permission collection that is associated with this userId, <code>null</code> if the
   *     parameter is <code>null</code>
   */
  public Permissions getPermissions(String userId) {
    if (userId == null) {
      return null;
    }
    synchronized (m_storeLock) {
      return m_store.get(userId.toLowerCase());
    }
  }

  /**
   * associate a permission collection with this userId
   *
   * @param userId if userId is <code>null</code> the method does nothing
   */
  public void setPermissions(String userId, Permissions p) {
    if (userId == null) {
      return;
    }
    synchronized (m_storeLock) {
      if (p == null) {
        p = new Permissions();
        p.setReadOnly();
      }
      m_store.put(userId.toLowerCase(), p);
    }
  }

  /** clears the cache */
  public void clearCache() {
    clearCacheOfUserIds(getUserIds());
  }

  /**
   * Clears the cache for a set of userIds and sends a notification for these users.
   *
   * @param userIds derived from the Subject, see{@link
   *     IAccessControlService#getUserIdOfCurrentSubject()}
   */
  public void clearCacheOfUserIds(Collection<String> userIds0) {
    Set<String> userIds = CollectionUtility.hashSetWithoutNullElements(userIds0);
    if (userIds.isEmpty()) {
      return;
    }
    synchronized (m_storeLock) {
      for (String userId : userIds) {
        if (userId != null) {
          m_store.remove(userId.toLowerCase());
        }
      }
    }
  }

  public Collection<String> getUserIds() {
    synchronized (m_storeLock) {
      return CollectionUtility.hashSet(m_store.keySet());
    }
  }
}
class ConcurrencyQueue {
  private static final IScoutLogger LOG = ScoutLogManager.getLogger(ConcurrencyQueue.class);
  private static long seqNoProvider;

  private long m_seqNo;
  private Object m_queueChangeLock;
  private boolean m_closed;
  private String m_name;
  private String m_infoCached;
  private Object m_listLock = new Object();
  private LinkedList<Runnable> m_list = new LinkedList<Runnable>();
  private LinkedList<Runnable> m_dispatchingList = new LinkedList<Runnable>();

  public ConcurrencyQueue(String name, Object queueChangeLock) {
    m_seqNo = seqNoProvider++;
    if (queueChangeLock == null) {
      queueChangeLock = new Object();
    }
    m_queueChangeLock = queueChangeLock;
    m_name = name;
    updateInternalInfo();
  }

  public boolean isEmpty() {
    synchronized (m_listLock) {
      return m_list.size() + m_dispatchingList.size() == 0;
    }
  }

  public void close() {
    m_closed = true;
    synchronized (m_listLock) {
      m_listLock.notifyAll();
    }
    synchronized (m_queueChangeLock) {
      m_queueChangeLock.notifyAll();
    }
  }

  public boolean isClosed() {
    return m_closed;
  }

  public void add(Runnable r) {
    if (r != null) {
      synchronized (m_listLock) {
        if (LOG.isDebugEnabled()) {
          LOG.debug(m_infoCached + " add: " + r);
        }
        m_list.add(r);
        updateInternalInfo();
        m_listLock.notifyAll();
      }
      synchronized (m_queueChangeLock) {
        m_queueChangeLock.notifyAll();
      }
    }
  }

  /** @return true if element could be removed */
  public boolean remove(Runnable r) {
    if (r != null) {
      boolean ok;
      synchronized (m_listLock) {
        ok = m_list.remove(r);
        updateInternalInfo();
        m_listLock.notifyAll();
      }
      synchronized (m_queueChangeLock) {
        m_queueChangeLock.notifyAll();
      }
      return ok;
    }
    return false;
  }

  public boolean isPending(Runnable r) {
    if (r != null) {
      synchronized (m_listLock) {
        return m_dispatchingList.contains(r) || m_list.contains(r);
      }
    }
    return false;
  }

  /** does not block */
  public boolean hasNext() {
    synchronized (m_listLock) {
      return !m_list.isEmpty();
    }
  }

  public List<Runnable> removeAllJobs() {
    synchronized (m_listLock) {
      ArrayList<Runnable> a = new ArrayList<Runnable>(m_list);
      m_list.clear();
      return a;
    }
  }

  /** block at most waitTime millis */
  public boolean hasNext(long waitTime) {
    long deadline = System.currentTimeMillis() + waitTime;
    synchronized (m_listLock) {
      while (m_list.isEmpty()) {
        long dt = deadline - System.currentTimeMillis();
        if (dt > 0) {
          try {
            m_listLock.wait(dt);
          } catch (InterruptedException e) {
          }
        } else {
          break;
        }
      }
      return !m_list.isEmpty();
    }
  }

  public void dispatchNext() {
    Runnable r = null;
    synchronized (m_listLock) {
      if (!m_list.isEmpty()) {
        r = m_list.removeFirst();
        m_dispatchingList.add(r);
      }
    }
    if (r != null) {
      try {
        long startTime = System.currentTimeMillis();
        if (LOG.isDebugEnabled()) {
          LOG.debug(m_infoCached + " dispatchStart: " + r);
        }
        r.run();
        if (LOG.isDebugEnabled()) {
          LOG.debug(
              m_infoCached
                  + " dispatchEnd "
                  + (System.currentTimeMillis() - startTime)
                  + " ms:   "
                  + r);
        }
      } catch (Throwable t) {
        LOG.error(null, t);
      }
      synchronized (m_listLock) {
        m_dispatchingList.remove(r);
        updateInternalInfo();
        m_listLock.notifyAll();
      }
      synchronized (m_queueChangeLock) {
        m_queueChangeLock.notifyAll();
      }
    }
  }

  private void updateInternalInfo() {
    int a = m_list.size();
    int b = m_dispatchingList.size();
    m_infoCached = m_name + "#" + m_seqNo + "[" + a + (b > 0 ? "+" + b : "") + " jobs]";
  }

  @Override
  public String toString() {
    return m_infoCached;
  }
}