gt:         int indexOpenBracket = label.indexOf("[");
context: /**
   * Copyright (c) 2014-2016 by the respective copyright holders.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   */
  package org.eclipse.smarthome.ui.internal.items;
  
  import java.util.Calendar;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import org.eclipse.emf.common.util.BasicEList;
  import org.eclipse.emf.common.util.EList;
  import org.eclipse.smarthome.core.common.registry.RegistryChangeListener;
  import org.eclipse.smarthome.core.items.GenericItem;
  import org.eclipse.smarthome.core.items.GroupItem;
  import org.eclipse.smarthome.core.items.Item;
  import org.eclipse.smarthome.core.items.ItemNotFoundException;
  import org.eclipse.smarthome.core.items.ItemNotUniqueException;
  import org.eclipse.smarthome.core.items.ItemRegistry;
  import org.eclipse.smarthome.core.library.items.CallItem;
  import org.eclipse.smarthome.core.library.items.ColorItem;
  import org.eclipse.smarthome.core.library.items.ContactItem;
  import org.eclipse.smarthome.core.library.items.DateTimeItem;
  import org.eclipse.smarthome.core.library.items.DimmerItem;
  import org.eclipse.smarthome.core.library.items.LocationItem;
  import org.eclipse.smarthome.core.library.items.NumberItem;
  import org.eclipse.smarthome.core.library.items.PlayerItem;
  import org.eclipse.smarthome.core.library.items.RollershutterItem;
  import org.eclipse.smarthome.core.library.items.StringItem;
  import org.eclipse.smarthome.core.library.items.SwitchItem;
  import org.eclipse.smarthome.core.library.types.DateTimeType;
  import org.eclipse.smarthome.core.library.types.DecimalType;
  import org.eclipse.smarthome.core.library.types.NextPreviousType;
  import org.eclipse.smarthome.core.library.types.PlayPauseType;
  import org.eclipse.smarthome.core.transform.TransformationException;
  import org.eclipse.smarthome.core.transform.TransformationHelper;
  import org.eclipse.smarthome.core.transform.TransformationService;
  import org.eclipse.smarthome.core.types.State;
  import org.eclipse.smarthome.core.types.StateDescription;
  import org.eclipse.smarthome.core.types.Type;
  import org.eclipse.smarthome.core.types.UnDefType;
  import org.eclipse.smarthome.model.sitemap.ColorArray;
  import org.eclipse.smarthome.model.sitemap.Default;
  import org.eclipse.smarthome.model.sitemap.Group;
  import org.eclipse.smarthome.model.sitemap.LinkableWidget;
  import org.eclipse.smarthome.model.sitemap.Mapping;
  import org.eclipse.smarthome.model.sitemap.Sitemap;
  import org.eclipse.smarthome.model.sitemap.SitemapFactory;
  import org.eclipse.smarthome.model.sitemap.Slider;
  import org.eclipse.smarthome.model.sitemap.Switch;
  import org.eclipse.smarthome.model.sitemap.VisibilityRule;
  import org.eclipse.smarthome.model.sitemap.Widget;
  import org.eclipse.smarthome.ui.internal.UIActivator;
  import org.eclipse.smarthome.ui.items.ItemUIProvider;
  import org.eclipse.smarthome.ui.items.ItemUIRegistry;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * This class provides a simple way to ask different item providers by a
   * single method call, i.e. the consumer does not need to iterate over all
   * registered providers as this is done inside this class.
   *
   * @author Kai Kreuzer - Initial contribution and API
   * @author Chris Jackson
   *
   */
  public class ItemUIRegistryImpl implements ItemUIRegistry {
  
      private final Logger logger = LoggerFactory.getLogger(ItemUIRegistryImpl.class);
  
      /* the image location inside the installation folder */
      protected static final String IMAGE_LOCATION = "./webapps/images/";
  
      /* RegEx to extract and parse a function String <code>'\[(.*?)\((.*)\):(.*)\]'</code> */
      protected static final Pattern EXTRACT_TRANSFORMFUNCTION_PATTERN = Pattern.compile("\\[(.*?)\\((.*)\\):(.*)\\]");
  
      /* RegEx to identify format patterns. See java.util.Formatter#formatSpecifier (without the '%' at the very end). */
      protected static final String IDENTIFY_FORMAT_PATTERN_PATTERN = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z])";
  
      protected Set<ItemUIProvider> itemUIProviders = new HashSet<ItemUIProvider>();
  
      protected ItemRegistry itemRegistry;
  
      public ItemUIRegistryImpl() {
      }
  
      public void setItemRegistry(ItemRegistry itemRegistry) {
          this.itemRegistry = itemRegistry;
      }
  
      public void unsetItemRegistry(ItemRegistry itemRegistry) {
          this.itemRegistry = null;
      }
  
      public void addItemUIProvider(ItemUIProvider itemUIProvider) {
          itemUIProviders.add(itemUIProvider);
      }
  
      public void removeItemUIProvider(ItemUIProvider itemUIProvider) {
          itemUIProviders.remove(itemUIProvider);
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public String getCategory(String itemName) {
          for (ItemUIProvider provider : itemUIProviders) {
              String currentCategory = provider.getCategory(itemName);
              if (currentCategory != null) {
                  return currentCategory;
              }
          }
  
          // use the category, if defined
          String category = getItemCategory(itemName);
          if (category != null) {
              return category.toLowerCase();
          }
  
          // do some reasonable default
          // try to get the item type from the item name
          Class<? extends Item> itemType = getItemType(itemName);
          if (itemType == null) {
              return null;
          }
  
          // we handle items here that have no specific widget,
          // e.g. the default widget of a rollerblind is "Switch".
          // We want to provide a dedicated default category for it
          // like "rollerblind".
          if (itemType.equals(NumberItem.class) || itemType.equals(ContactItem.class)
                  || itemType.equals(RollershutterItem.class)) {
              return itemType.getSimpleName().replace("Item", "").toLowerCase();
          }
          return null;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public String getLabel(String itemName) {
          for (ItemUIProvider provider : itemUIProviders) {
              String currentLabel = provider.getLabel(itemName);
              if (currentLabel != null) {
                  return currentLabel;
              }
          }
          try {
              Item item = getItem(itemName);
              if (item.getLabel() != null) {
                  return item.getLabel();
              }
          } catch (ItemNotFoundException e) {
          }
  
          return null;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public Widget getWidget(String itemName) {
          for (ItemUIProvider provider : itemUIProviders) {
              Widget currentWidget = provider.getWidget(itemName);
              if (currentWidget != null) {
                  return resolveDefault(currentWidget);
              }
          }
          return null;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public Widget getDefaultWidget(Class<? extends Item> itemType, String itemName) {
          for (ItemUIProvider provider : itemUIProviders) {
              Widget widget = provider.getDefaultWidget(itemType, itemName);
              if (widget != null) {
                  return widget;
              }
          }
  
          // do some reasonable default, if no provider had an answer
          // if the itemType is not defined, try to get it from the item name
          if (itemType == null) {
              itemType = getItemType(itemName);
          }
          if (itemType == null) {
              return null;
          }
  
          if (itemType.equals(SwitchItem.class)) {
              return SitemapFactory.eINSTANCE.createSwitch();
          }
          if (itemType.equals(GroupItem.class)) {
              return SitemapFactory.eINSTANCE.createGroup();
          }
          if (NumberItem.class.isAssignableFrom(itemType)) {
              return SitemapFactory.eINSTANCE.createText();
          }
          if (itemType.equals(ContactItem.class)) {
              return SitemapFactory.eINSTANCE.createText();
          }
          if (itemType.equals(DateTimeItem.class)) {
              return SitemapFactory.eINSTANCE.createText();
          }
          if (itemType.equals(RollershutterItem.class)) {
              return SitemapFactory.eINSTANCE.createSwitch();
          }
          if (itemType.equals(StringItem.class)) {
              return SitemapFactory.eINSTANCE.createText();
          }
          if (itemType.equals(LocationItem.class)) {
              return SitemapFactory.eINSTANCE.createText();
          }
          if (itemType.equals(CallItem.class)) {
              return SitemapFactory.eINSTANCE.createText();
          }
          if (itemType.equals(DimmerItem.class)) {
              Slider slider = SitemapFactory.eINSTANCE.createSlider();
              slider.setSwitchEnabled(true);
              return slider;
          }
          if (itemType.equals(ColorItem.class)) {
              return SitemapFactory.eINSTANCE.createColorpicker();
          }
          if (itemType.equals(PlayerItem.class)) {
              return createPlayerButtons();
          }
  
          return null;
      }
  
      private Switch createPlayerButtons() {
          Switch playerItemSwitch = SitemapFactory.eINSTANCE.createSwitch();
          List<Mapping> mappings = playerItemSwitch.getMappings();
          Mapping commandMapping = null;
          mappings.add(commandMapping = SitemapFactory.eINSTANCE.createMapping());
          commandMapping.setCmd(NextPreviousType.PREVIOUS.name());
          commandMapping.setLabel("<<");
          mappings.add(commandMapping = SitemapFactory.eINSTANCE.createMapping());
          commandMapping.setCmd(PlayPauseType.PAUSE.name());
          commandMapping.setLabel("||");
          mappings.add(commandMapping = SitemapFactory.eINSTANCE.createMapping());
          commandMapping.setCmd(PlayPauseType.PLAY.name());
          commandMapping.setLabel(">");
          mappings.add(commandMapping = SitemapFactory.eINSTANCE.createMapping());
          commandMapping.setCmd(NextPreviousType.NEXT.name());
          commandMapping.setLabel(">>");
          return playerItemSwitch;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public String getLabel(Widget w) {
          String label = getLabelFromWidget(w);
  
          // now insert the value, if the state is a string or decimal value and there is some formatting pattern defined
          // in the label
          // (i.e. it contains at least a %)
          String itemName = w.getItem();
          if (itemName != null) {
              State state = null;
              String formatPattern = null;
  
              try {
                  final Item item = getItem(itemName);
                  if (getFormatPattern(label) == null) {
                      final StateDescription stateDescription = item.getStateDescription();
                      if (stateDescription != null) {
                          final String pattern = stateDescription.getPattern();
                          if (pattern != null) {
                              label = label + " [" + pattern + "]";
                          }
                      }
                  }
  
                  String updatedPattern = getFormatPattern(label);
                  if (updatedPattern != null) {
                      formatPattern = updatedPattern;
  
                      // TODO: TEE: we should find a more generic solution here! When
                      // using indexes in formatString this 'contains' will fail again
                      // and will cause an 'java.util.IllegalFormatConversionException:
                      // d != java.lang.String' later on when trying to format a String
                      // as %d (number).
                      if (label.contains("%d")) {
                          // a number is requested
                          state = item.getState();
                          if (!(state instanceof DecimalType)) {
                              state = item.getStateAs(DecimalType.class);
                          }
                      } else {
                          state = item.getState();
                      }
                  }
              } catch (ItemNotFoundException e) {
                  logger.error("Cannot retrieve item for widget {}", w.eClass().getInstanceTypeName());
              }
  
              if (formatPattern != null) {
                  if (state == null || state instanceof UnDefType) {
                      formatPattern = formatUndefined(formatPattern);
                  } else if (state instanceof Type) {
                      // The following exception handling has been added to work around a Java bug with formatting
                      // numbers. See http://bugs.sun.com/view_bug.do?bug_id=6476425
                      // Without this catch, the whole sitemap, or page can not be displayed!
                      // This also handles IllegalFormatConversionException, which is a subclass of IllegalArgument.
                      try {
                          formatPattern = ((Type) state).format(formatPattern);
                      } catch (IllegalArgumentException e) {
                          logger.warn("Exception while formatting value '{}' of item {} with format '{}': {}", state,
                                  itemName, formatPattern, e);
                          formatPattern = new String("Err");
                      }
                  }
  
                  label = label.trim();
                  label = label.substring(0, label.indexOf("[") + 1) + formatPattern + "]";
              }
          }
  
          label = transform(label);
  
          return label;
      }
  
      private String getFormatPattern(String label) {
          label = label.trim();
  