Package org.springframework.xd.module.options

Source Code of org.springframework.xd.module.options.FlattenedCompositeModuleOptionsMetadata

/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.xd.module.options;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.MapBindingResult;

/**
* A composite {@link ModuleOptionsMetadata} made of several {@link ModuleOptionsMetadata} that will appear "flattened".
*
* @author Eric Bottard
* @author David Turanski
*/
public class FlattenedCompositeModuleOptionsMetadata implements ModuleOptionsMetadata {

  /**
   *
   */
  private static final String INPUT_TYPE = "inputType";

  private static final Log logger = LogFactory.getLog(FlattenedCompositeModuleOptionsMetadata.class);

  private Map<String, ModuleOption> options = new LinkedHashMap<String, ModuleOption>();

  private Map<ModuleOptionsMetadata, Set<String>> momToSupportedOptions = new LinkedHashMap<ModuleOptionsMetadata, Set<String>>();

  public FlattenedCompositeModuleOptionsMetadata(List<? extends ModuleOptionsMetadata> delegates) {
    ModuleOption inputType = null;
    for (final ModuleOptionsMetadata delegate : delegates) {
      Set<String> optionNames = new HashSet<String>();
      momToSupportedOptions.put(delegate, optionNames);
      for (ModuleOption option : delegate) {
        if (options.get(option.getName()) != null) {
          /*
           * XD-1472: InputType treated as a special case. If the module defines a default, do not replace it
           * with a null value (the global default)
           */
          if (option.getName().equals(INPUT_TYPE)) {
            inputType = options.get(option.getName());
          }
          else {
            reportOptionCollision(delegates, option.getName());
          }
        }

        options.put(option.getName(), option.getName().equals(INPUT_TYPE) && inputType != null ? inputType
            : option);
        optionNames.add(option.getName());
      }
    }
  }

  private void reportOptionCollision(List<? extends ModuleOptionsMetadata> delegates, String optionName) {
    List<ModuleOptionsMetadata> offenders = new ArrayList<ModuleOptionsMetadata>();
    for (final ModuleOptionsMetadata delegate : delegates) {
      for (ModuleOption option : delegate) {
        if (option.getName().equals(optionName)) {
          offenders.add(delegate);
          break;
        }
      }
    }
    throw new IllegalArgumentException(String.format(
        "Module option named '%s' is present in several delegates: %s",
        optionName, offenders));
  }

  @Override
  public Iterator<ModuleOption> iterator() {
    return options.values().iterator();
  }

  @Override
  public ModuleOptions interpolate(Map<String, String> raw) throws BindException {


    @SuppressWarnings("serial")
    Map<ModuleOptionsMetadata, Map<String, String>> distributed = new HashMap<ModuleOptionsMetadata, Map<String, String>>() {

      @Override
      public Map<String, String> get(Object key) {
        Map<String, String> result = super.get(key);
        if (result == null) {
          result = new HashMap<String, String>();
          this.put((ModuleOptionsMetadata) key, result);
        }
        return result;
      }
    };

    Map<String, String> copy = new HashMap<String, String>(raw);

    // First, distribute raw input values to their corresponding MOM
    for (String key : raw.keySet()) {
      for (ModuleOptionsMetadata mom : momToSupportedOptions.keySet()) {
        Set<String> optionNames = momToSupportedOptions.get(mom);
        if (optionNames.contains(key)) {
          distributed.get(mom).put(key, copy.remove(key));
          break;
        }
      }
    }

    if (copy.size() > 0) {
      // We're just interested in a container for errors
      BindingResult bindingResult = new MapBindingResult(new HashMap<String, Object>(), "flattened");
      for (String pty : copy.keySet()) {
        bindingResult.addError(new FieldError("flattened", pty, String.format(
            "option named '%s' is not supported", pty)));
      }
      throw new BindException(bindingResult);
    }

    // Then, interpolate per-MOM and remember which MOM is responsible for which exposed value
    // TODO: should be multimap, as several MOMs could expose the same value
    final Map<String, ModuleOptions> nameToOptions = new HashMap<String, ModuleOptions>();

    for (ModuleOptionsMetadata mom : momToSupportedOptions.keySet()) {
      Map<String, String> rawValuesSubset = distributed.get(mom);
      ModuleOptions mo = mom.interpolate(rawValuesSubset);
      EnumerablePropertySource<?> propertySource = mo.asPropertySource();
      for (String optionName : propertySource.getPropertyNames()) {
        /*
         * XD-1472: InputType treated as a special case. If the module defines a default value, do not replace
         * it with a null value (the global default)
         */

        if (optionName.equals(INPUT_TYPE)) {
          if (propertySource.getProperty(optionName) != null) {
            nameToOptions.put(optionName, mo);
          }
        }
        else {
          nameToOptions.put(optionName, mo);
        }
      }
    }

    final Set<ModuleOptions> uniqueModuleOptions = new HashSet<ModuleOptions>(nameToOptions.values());

    return new ModuleOptions() {

      @Override
      public EnumerablePropertySource<FlattenedCompositeModuleOptionsMetadata> asPropertySource() {
        String psName = String.format("flattened-%d",
            System.identityHashCode(FlattenedCompositeModuleOptionsMetadata.this));
        return new EnumerablePropertySource<FlattenedCompositeModuleOptionsMetadata>(psName,
            FlattenedCompositeModuleOptionsMetadata.this) {

          @Override
          public String[] getPropertyNames() {
            String[] result = nameToOptions.keySet().toArray(new String[nameToOptions.keySet().size()]);
            FlattenedCompositeModuleOptionsMetadata.logger.debug(String.format(
                "returning propertyNames: %s",
                StringUtils.arrayToCommaDelimitedString(result)));
            return result;
          }

          @Override
          public Object getProperty(String name) {
            ModuleOptions moduleOptions = nameToOptions.get(name);
            return moduleOptions == null ? null : moduleOptions.asPropertySource().getProperty(name);
          }
        };
      }

      @Override
      public String[] profilesToActivate() {
        List<String> result = new ArrayList<String>();
        for (ModuleOptions delegate : uniqueModuleOptions) {
          result.addAll(Arrays.asList(delegate.profilesToActivate()));
        }
        return result.toArray(new String[result.size()]);
      }

      @Override
      public void validate() {
        for (ModuleOptions delegate : uniqueModuleOptions) {
          delegate.validate();
        }
      }
    };
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder(getClass().getSimpleName());
    for (ModuleOptionsMetadata sub : momToSupportedOptions.keySet()) {
      sb.append("\n").append(momToSupportedOptions.get(sub)).append(" => ").append(sub);
    }
    return sb.toString();
  }
}
TOP

Related Classes of org.springframework.xd.module.options.FlattenedCompositeModuleOptionsMetadata

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.