gt:                 if (someStation.getTitle().equals(station)) {
context: /**
   * Copyright (c) 2010-2019 Contributors to the openHAB project
   *
   * See the NOTICE file(s) distributed with this work for additional
   * information.
   *
   * This program and the accompanying materials are made available under the
   * terms of the Eclipse Public License 2.0 which is available at
   * http://www.eclipse.org/legal/epl-2.0
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  package org.openhab.binding.sonos.internal;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  
  import org.apache.commons.lang.StringUtils;
  import org.joda.time.DateTime;
  import org.joda.time.Duration;
  import org.joda.time.Period;
  import org.joda.time.format.DateTimeFormat;
  import org.joda.time.format.DateTimeFormatter;
  import org.joda.time.format.PeriodFormatter;
  import org.joda.time.format.PeriodFormatterBuilder;
  import org.openhab.binding.sonos.SonosCommandType;
  import org.openhab.binding.sonos.internal.SonosBinding.SonosZonePlayerState;
  import org.openhab.io.net.http.HttpUtil;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.teleal.cling.UpnpService;
  import org.teleal.cling.controlpoint.ActionCallback;
  import org.teleal.cling.controlpoint.SubscriptionCallback;
  import org.teleal.cling.model.action.ActionArgumentValue;
  import org.teleal.cling.model.action.ActionException;
  import org.teleal.cling.model.action.ActionInvocation;
  import org.teleal.cling.model.gena.CancelReason;
  import org.teleal.cling.model.gena.GENASubscription;
  import org.teleal.cling.model.message.UpnpResponse;
  import org.teleal.cling.model.meta.Action;
  import org.teleal.cling.model.meta.RemoteDevice;
  import org.teleal.cling.model.meta.Service;
  import org.teleal.cling.model.meta.StateVariable;
  import org.teleal.cling.model.meta.StateVariableTypeDetails;
  import org.teleal.cling.model.state.StateVariableValue;
  import org.teleal.cling.model.types.Datatype;
  import org.teleal.cling.model.types.InvalidValueException;
  import org.teleal.cling.model.types.UDAServiceId;
  import org.teleal.cling.model.types.UDN;
  import org.teleal.cling.model.types.UnsignedIntegerFourBytes;
  import org.xml.sax.SAXException;
  
  /**
   * Internal data structure which carries the connection details of one Sonos player
   * (there could be several)
   *
   * @author Karel Goderis
   * @since 1.1.0
   *
   */
  class SonosZonePlayer {
  
      private static Logger logger = LoggerFactory.getLogger(SonosZonePlayer.class);
  
      protected final int interval = 600;
      private boolean isConfigured = false;
  
      /** the default socket timeout when requesting an url */
      private static final int SO_TIMEOUT = 5000;
  
      private RemoteDevice device = null;
      private UDN udn;
      private String id;
      private DateTime lastOPMLQuery;
      private SonosZonePlayerState savedState = null;
  
      static protected UpnpService upnpService;
      protected SonosBinding sonosBinding;
  
      private Map<String, StateVariableValue> stateMap = Collections
              .synchronizedMap(new HashMap<String, StateVariableValue>());
  
      /**
       * @return the stateMap
       */
      public Map<String, StateVariableValue> getStateMap() {
          return stateMap;
      }
  
      public boolean isConfigured() {
          return isConfigured;
      }
  
      SonosZonePlayer(String id, SonosBinding binding) {
  
          if (binding != null) {
              this.id = id;
              sonosBinding = binding;
          }
      }
  
      private void enableGENASubscriptions() {
  
          if (device != null && isConfigured()) {
  
              // Create a GENA subscription of each service for this device, if supported by the device
              List<SonosCommandType> subscriptionCommands = SonosCommandType.getSubscriptions();
              List<String> addedSubscriptions = new ArrayList<String>();
  
              for (SonosCommandType c : subscriptionCommands) {
                  Service service = device.findService(new UDAServiceId(c.getService()));
                  if (service != null && !addedSubscriptions.contains(c.getService())) {
                      SonosPlayerSubscriptionCallback callback = new SonosPlayerSubscriptionCallback(service, interval);
                      addedSubscriptions.add(c.getService());
                      upnpService.getControlPoint().execute(callback);
                  }
              }
          }
      }
  
      protected boolean isUpdatedValue(String valueName, StateVariableValue newValue) {
          if (newValue != null && valueName != null) {
              StateVariableValue oldValue = stateMap.get(valueName);
  
              if (newValue.getValue() == null) {
                  // we will *not* store an empty value, thank you.
                  return false;
              } else {
                  if (oldValue == null) {
                      // there was nothing stored before
                      return true;
                  } else {
                      if (oldValue.getValue() == null) {
                          // something was defined, but no value present
                          return true;
                      } else {
                          if (newValue.getValue().equals(oldValue.getValue())) {
                              return false;
                          } else {
                              return true;
                          }
                      }
                  }
              }
          }
  
          return false;
      }
  
      protected void processStateVariableValue(String valueName, StateVariableValue newValue) {
          if (newValue != null && isUpdatedValue(valueName, newValue)) {
              Map<String, StateVariableValue> mapToProcess = new HashMap<String, StateVariableValue>();
              mapToProcess.put(valueName, newValue);
              stateMap.putAll(mapToProcess);
              sonosBinding.processVariableMap(device, mapToProcess);
          }
      }
  
      /**
       * @return the device
       */
      public RemoteDevice getDevice() {
          return device;
      }
  
      /**
       * @param device the device to set
       */
      public void setDevice(RemoteDevice device) {
          this.device = device;
          if (upnpService != null && device != null) {
              isConfigured = true;
              enableGENASubscriptions();
          }
      }
  
      public class SonosPlayerSubscriptionCallback extends SubscriptionCallback {
  
          public SonosPlayerSubscriptionCallback(Service service) {
              super(service);
          }
  
          public SonosPlayerSubscriptionCallback(Service service, int requestedDurationSeconds) {
              super(service, requestedDurationSeconds);
          }
  
          @Override
          public void established(GENASubscription sub) {
          }
  
          @Override
          protected void failed(GENASubscription subscription, UpnpResponse responseStatus, Exception exception,
                  String defaultMsg) {
          }
  
          @Override
          public void eventReceived(GENASubscription sub) {
  
              // get the device linked to this service linked to this subscription
              Map<String, StateVariableValue> values = sub.getCurrentValues();
              Map<String, StateVariableValue> mapToProcess = new HashMap<String, StateVariableValue>();
              Map<String, StateVariableValue> parsedValues = null;
  
              // now, lets deal with the specials - some UPNP responses require some XML parsing
              // or we need to update our internal data structure
              // or are things we want to store for further reference
  
              for (String stateVariable : values.keySet()) {
                  if (stateVariable.equals("LastChange") && service.getServiceType().getType().equals("AVTransport")) {
                      try {
                          parsedValues = SonosXMLParser.getAVTransportFromXML(values.get(stateVariable).toString());
                          for (String someValue : parsedValues.keySet()) {
                              // logger.debug("Lastchange parsed into {}:{}",someValue,parsedValues.get(someValue));
                              if (isUpdatedValue(someValue, parsedValues.get(someValue))) {
                                  mapToProcess.put(someValue, parsedValues.get(someValue));
                              }
                          }
                      } catch (SAXException e) {
                          logger.error("Could not parse AVTransport from String {}",
                                  values.get(stateVariable).toString());
                      }
  
                  } else if (stateVariable.equals("LastChange")
                          && service.getServiceType().getType().equals("RenderingControl")) {
                      try {
                          parsedValues = SonosXMLParser.getRenderingControlFromXML(values.get(stateVariable).toString());
                          for (String someValue : parsedValues.keySet()) {
                              if (isUpdatedValue(someValue, parsedValues.get(someValue))) {
                                  mapToProcess.put(someValue, parsedValues.get(someValue));
                              }
                          }
                      } catch (SAXException e) {
                          logger.error("Could not parse RenderingControl from String {}",
                                  values.get(stateVariable).toString());
                      }
                  } else if (isUpdatedValue(stateVariable, values.get(stateVariable))) {
                      mapToProcess.put(stateVariable, values.get(stateVariable));
                  }
  
              }
  
              if (isConfigured) {
                  stateMap.putAll(mapToProcess);
                  sonosBinding.processVariableMap(device, mapToProcess);
              }
          }
  
          @Override
          public void eventsMissed(GENASubscription sub, int numberOfMissedEvents) {
              logger.warn("Missed events: " + numberOfMissedEvents);
          }
  
          @Override
          protected void ended(GENASubscription subscription, CancelReason reason, UpnpResponse responseStatus) {
  
              if (device != null && isConfigured()) {
                  // rebooting the GENA subscription
                  Service service = subscription.getService();
                  SonosPlayerSubscriptionCallback callback = new SonosPlayerSubscriptionCallback(service, interval);
                  upnpService.getControlPoint().execute(callback);
  
              }
          }
      }
  
      public void setService(UpnpService service) {
          if (upnpService == null) {
              upnpService = service;
          }
          if (upnpService != null && device != null) {
              isConfigured = true;
              enableGENASubscriptions();
          }
  
      }
  
      public String getModel() {
          if (device != null) {
              return device.getDetails().getModelDetails().getModelNumber();
          } else {
              return "Unknown";
          }
      }
  
      /**
       * @return the udn
       */
      public UDN getUdn() {
          return udn;
      }
  
      /**
       * @param udn the udn to set
       */
      public void setUdn(UDN udn) {
          this.udn = udn;
      }
  
      public String getId() {
          return id;
      }
  
      @Override
      public String toString() {
          return "Sonos [udn=" + udn + ", device=" + device + "]";
      }
  
      public boolean play() {
          if (isConfigured()) {
              Service service = device.findService(new UDAServiceId("AVTransport"));
              Action action = service.getAction("Play");
              ActionInvocation invocation = new ActionInvocation(action);
  
              invocation.setInput("Speed", "1");
  
              executeActionInvocation(invocation);
  
              return true;
          } else {
              return false;
          }
      }
  
      public boolean playRadio(String station) {
  
          if (isConfigured()) {
  
              List<SonosEntry> stations = getFavoriteRadios();
  
              SonosEntry theEntry = null;
              // search for the appropriate radio based on its name (title)
              for (SonosEntry someStation : stations) {
  