/* (non-Javadoc)
   * @see org.jasig.portal.layout.om.IStylesheetUserPreferences#removeLayoutAttribute(java.lang.String, java.lang.String)
   */
  @Override
  public String removeLayoutAttribute(String nodeId, String name) {
    final ILayoutAttributeDescriptor layoutAttributeDescriptor =
        this.stylesheetDescriptor.getLayoutAttributeDescriptor(name);
    if (layoutAttributeDescriptor == null) {
      logger.warn(
          "Attempted to remove layout attribute {} for ID=\"{}\" but no such stylesheet parameter is defined in stylesheet descriptor {}. It will be ignored.",
          new Object[] {name, nodeId, this.stylesheetDescriptor.getName()});
      return null;
    }

    final Scope scope = this.getWriteScope(layoutAttributeDescriptor);
    final IStylesheetUserPreferences stylesheetUserPreferences =
        this.getStylesheetUserPreferences(scope, false);
    if (stylesheetUserPreferences != null) {
      final String layoutAttribute = stylesheetUserPreferences.removeLayoutAttribute(nodeId, name);
      if (layoutAttribute != null) {
        return layoutAttribute;
      }
    }

    if (this.distributedStylesheetUserPreferences != null) {
      return this.distributedStylesheetUserPreferences.removeLayoutAttribute(nodeId, name);
    }

    return null;
  }
  @Override
  public long getId() {
    final IStylesheetUserPreferences stylesheetUserPreferences =
        this.getStylesheetUserPreferences(Scope.PERSISTENT, false);
    if (stylesheetUserPreferences != null) {
      return stylesheetUserPreferences.getId();
    }

    return -1;
  }
  /* (non-Javadoc)
   * @see org.jasig.portal.layout.om.IStylesheetUserPreferences#getLayoutAttributes(java.lang.String)
   */
  @SuppressWarnings("unchecked")
  @Override
  public Map<String, String> getLayoutAttributes(final String nodeId) {
    final Map<String, String> distributedLayoutAttributes;
    if (distributedStylesheetUserPreferences != null) {
      distributedLayoutAttributes =
          distributedStylesheetUserPreferences.getLayoutAttributes(nodeId);
    } else {
      distributedLayoutAttributes = Collections.emptyMap();
    }

    return this.buildCompositeMap(
        new Function<Object, Map<String, String>>() {
          @Override
          public Map<String, String> apply(Object input) {
            return new LinkedHashMap<String, String>();
          }
        },
        new PreferenceValueFunction<Map<String, String>>() {
          @Override
          public Map<String, String> getPreferences(IStylesheetUserPreferences preferences) {
            return preferences.getLayoutAttributes(nodeId);
          }
        },
        stylesheetDescriptor.getLayoutAttributeDescriptors(),
        distributedLayoutAttributes);
  }
  /* (non-Javadoc)
   * @see org.jasig.portal.layout.om.IStylesheetUserPreferences#removeStylesheetParameter(java.lang.String)
   */
  @Override
  public String removeStylesheetParameter(String name) {
    final IStylesheetParameterDescriptor stylesheetParameterDescriptor =
        this.stylesheetDescriptor.getStylesheetParameterDescriptor(name);
    if (stylesheetParameterDescriptor == null) {
      logger.warn(
          "Attempted to remove stylesheet parameter {} but no such stylesheet parameter is defined in stylesheet descriptor {}. It will be ignored",
          new Object[] {name, this.stylesheetDescriptor.getName()});
      return null;
    }

    final Scope scope = this.getWriteScope(stylesheetParameterDescriptor);
    final IStylesheetUserPreferences stylesheetUserPreferences =
        this.getStylesheetUserPreferences(scope, false);
    if (stylesheetUserPreferences == null) {
      return null;
    }

    return stylesheetUserPreferences.removeStylesheetParameter(name);
  }
  /* (non-Javadoc)
   * @see org.jasig.portal.layout.om.IStylesheetUserPreferences#removeOutputProperty(java.lang.String)
   */
  @Override
  public String removeOutputProperty(String name) {
    final IOutputPropertyDescriptor outputPropertyDescriptor =
        this.stylesheetDescriptor.getOutputPropertyDescriptor(name);
    if (outputPropertyDescriptor == null) {
      logger.warn(
          "Attempted to remove output property {} but no such output property is defined in stylesheet descriptor {}. It will be ignored",
          new Object[] {name, this.stylesheetDescriptor.getName()});
      return null;
    }

    final Scope scope = this.getWriteScope(outputPropertyDescriptor);
    final IStylesheetUserPreferences stylesheetUserPreferences =
        this.getStylesheetUserPreferences(scope, false);
    if (stylesheetUserPreferences == null) {
      return null;
    }

    return stylesheetUserPreferences.removeOutputProperty(name);
  }
  /* (non-Javadoc)
   * @see org.jasig.portal.layout.om.IStylesheetUserPreferences#setLayoutAttribute(java.lang.String, java.lang.String, java.lang.String)
   */
  @Override
  public String setLayoutAttribute(String nodeId, String name, String value) {
    final ILayoutAttributeDescriptor layoutAttributeDescriptor =
        this.stylesheetDescriptor.getLayoutAttributeDescriptor(name);
    if (layoutAttributeDescriptor == null) {
      logger.warn(
          "Attempted to set layout attribute {}={} on node with ID=\"{}\" but no such stylesheet parameter is defined in stylesheet descriptor {}. It will be ignored.",
          new Object[] {name, value, nodeId, this.stylesheetDescriptor.getName()});
      return null;
    }

    final Scope scope = this.getWriteScope(layoutAttributeDescriptor);
    final IStylesheetUserPreferences stylesheetUserPreferences =
        this.getStylesheetUserPreferences(scope, true);

    final String defaultValue = layoutAttributeDescriptor.getDefaultValue();
    if (this.compareValues(value, defaultValue)) {
      return stylesheetUserPreferences.removeLayoutAttribute(nodeId, name);
    }

    return stylesheetUserPreferences.setLayoutAttribute(nodeId, name, value);
  }
  /* (non-Javadoc)
   * @see org.jasig.portal.layout.om.IStylesheetUserPreferences#setStylesheetParameter(java.lang.String, java.lang.String)
   */
  @Override
  public String setStylesheetParameter(String name, String value) {
    final IStylesheetParameterDescriptor stylesheetParameterDescriptor =
        this.stylesheetDescriptor.getStylesheetParameterDescriptor(name);
    if (stylesheetParameterDescriptor == null) {
      logger.warn(
          "Attempted to set stylesheet parameter {}={} but no such stylesheet parameter is defined in stylesheet descriptor {}. It will be ignored",
          new Object[] {name, value, this.stylesheetDescriptor.getName()});
      return null;
    }

    final Scope scope = this.getWriteScope(stylesheetParameterDescriptor);
    final IStylesheetUserPreferences stylesheetUserPreferences =
        this.getStylesheetUserPreferences(scope, true);

    final String defaultValue = stylesheetParameterDescriptor.getDefaultValue();
    if (this.compareValues(value, defaultValue)) {
      return stylesheetUserPreferences.removeStylesheetParameter(name);
    }

    return stylesheetUserPreferences.setStylesheetParameter(name, value);
  }
  @Override
  public String getOutputProperty(String name) {
    final IOutputPropertyDescriptor outputPropertyDescriptor =
        this.stylesheetDescriptor.getOutputPropertyDescriptor(name);
    if (outputPropertyDescriptor == null) {
      logger.warn(
          "Attempted to get output property {} but no such output property is defined in stylesheet descriptor {}. null will be returned",
          new Object[] {name, this.stylesheetDescriptor.getName()});
      return null;
    }

    final Scope scope = outputPropertyDescriptor.getScope();
    final IStylesheetUserPreferences stylesheetUserPreferences =
        this.getStylesheetUserPreferences(scope, false);
    if (stylesheetUserPreferences != null) {
      final String outputProperty = stylesheetUserPreferences.getOutputProperty(name);
      if (outputProperty != null) {
        return outputProperty;
      }
    }

    return outputPropertyDescriptor.getDefaultValue();
  }
  @Override
  public String getStylesheetParameter(String name) {
    final IStylesheetParameterDescriptor stylesheetParameterDescriptor =
        this.stylesheetDescriptor.getStylesheetParameterDescriptor(name);
    if (stylesheetParameterDescriptor == null) {
      logger.warn(
          "Attempted to get stylesheet parameter {} but no such stylesheet parameter is defined in stylesheet descriptor {}. null will be returned",
          new Object[] {name, this.stylesheetDescriptor.getName()});
      return null;
    }

    final Scope scope = stylesheetParameterDescriptor.getScope();
    final IStylesheetUserPreferences stylesheetUserPreferences =
        this.getStylesheetUserPreferences(scope, false);
    if (stylesheetUserPreferences != null) {
      final String stylesheetParameter = stylesheetUserPreferences.getStylesheetParameter(name);
      if (stylesheetParameter != null) {
        return stylesheetParameter;
      }
    }

    return stylesheetParameterDescriptor.getDefaultValue();
  }
  /**
   * Adds all layout attributes from the specified preferences into the composite map (as long as
   * they don't already exist). Uses {@link #getLayoutAttributes(String)}
   */
  protected void addLayoutAttributes(
      final Map<String, Map<String, String>> compositeMap,
      final IStylesheetUserPreferences stylesheetUserPreferences) {
    if (stylesheetUserPreferences == null) {
      return;
    }

    final Map<String, Map<String, String>> allLayoutAttributes =
        stylesheetUserPreferences.getAllLayoutAttributes();

    for (final String nodeId : allLayoutAttributes.keySet()) {
      if (compositeMap.containsKey(nodeId)) {
        continue;
      }

      final Map<String, String> layoutAttributes = this.getLayoutAttributes(nodeId);
      compositeMap.put(nodeId, layoutAttributes);
    }
  }