Package jodd.props

Source Code of jodd.props.PropsData

// Copyright (c) 2003-2014, Jodd Team (jodd.org). All Rights Reserved.

package jodd.props;

import jodd.util.StringPool;
import jodd.util.StringTemplateParser;
import jodd.util.StringUtil;
import jodd.util.Wildcard;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
* Props data storage for base and profile properties.
* Properties can be lookuped and modified only through this
* class.
*/
public class PropsData implements Cloneable {

  private static final int MAX_INNER_MACROS = 100;
  private static final String APPEND_SEPARATOR = ",";

  protected final HashMap<String, PropsEntry> baseProperties;
  protected final HashMap<String, Map<String, PropsEntry>> profileProperties;

  protected PropsEntry first;
  protected PropsEntry last;

  /**
   * If set, duplicate props will be appended to the end, separated by comma.
   */
  protected boolean appendDuplicateProps;

  /**
   * When set, missing macros will be replaces with an empty string.
   */
  protected boolean ignoreMissingMacros;

  /**
   * When set, empty properties will be skipped.
   */
  protected boolean skipEmptyProps = true;

  public PropsData() {
    this(new HashMap<String, PropsEntry>(), new HashMap<String, Map<String, PropsEntry>>());
  }

  protected PropsData(final HashMap<String, PropsEntry> properties, final HashMap<String, Map<String, PropsEntry>> profiles) {
    this.baseProperties = properties;
    this.profileProperties = profiles;
  }

  @Override
  public PropsData clone() {
    final HashMap<String, PropsEntry> newBase = new HashMap<String, PropsEntry>();
    final HashMap<String, Map<String, PropsEntry>> newProfiles = new HashMap<String, Map<String, PropsEntry>>();

    newBase.putAll(baseProperties);
    for (final Map.Entry<String, Map<String, PropsEntry>> entry : profileProperties.entrySet()) {
      final Map<String, PropsEntry> map = new HashMap<String, PropsEntry>(entry.getValue().size());
      map.putAll(entry.getValue());
      newProfiles.put(entry.getKey(), map);
    }

    final PropsData pd = new PropsData(newBase, newProfiles);
    pd.appendDuplicateProps = appendDuplicateProps;
    pd.ignoreMissingMacros = ignoreMissingMacros;
    pd.skipEmptyProps = skipEmptyProps;
    return pd;
  }

  // ---------------------------------------------------------------- misc

  /**
   * Puts key-value pair into the map, with respect of appending duplicate properties
   */
  protected void put(final String profile, final Map<String, PropsEntry> map, final String key, final String value, final boolean append) {
    String realValue = value;
    if (append || appendDuplicateProps) {
      PropsEntry pv = map.get(key);
      if (pv != null) {
        realValue = pv.value + APPEND_SEPARATOR + realValue;
      }
    }
    PropsEntry propsEntry = new PropsEntry(key, realValue, profile, this);

    // update position pointers
    if (first == null) {
      first = propsEntry;
    } else {
      last.next = propsEntry;
    }
    last = propsEntry;

    // add to the map
    map.put(key, propsEntry);
  }

  // ---------------------------------------------------------------- properties

  /**
   * Counts base properties.
   */
  public int countBaseProperties() {
    return baseProperties.size();
  }

  /**
   * Adds base property.
   */
  public void putBaseProperty(final String key, final String value, final boolean append) {
    put(null, baseProperties, key, value, append);
  }

  /**
   * Returns base property or <code>null</code> if it doesn't exist.
   */
  public PropsEntry getBaseProperty(final String key) {
    return baseProperties.get(key);
  }

  // ---------------------------------------------------------------- profiles

  /**
   * Counts profile properties. Note: this method is not
   * that easy on execution.
   */
  public int countProfileProperties() {
    final HashSet<String> profileKeys = new HashSet<String>();

    for (final Map<String, PropsEntry> map : profileProperties.values()) {
      for (final String key : map.keySet()) {
        if (!baseProperties.containsKey(key)) {
          profileKeys.add(key);
        }
      }
    }
    return profileKeys.size();
  }

  /**
   * Adds profile property.
   */
  public void putProfileProperty(final String key, final String value, final String profile, final boolean append) {
    Map<String, PropsEntry> map = profileProperties.get(profile);
    if (map == null) {
      map = new HashMap<String, PropsEntry>();
      profileProperties.put(profile, map);
    }
    put(profile, map, key, value, append);
  }

  /**
   * Returns profile property.
   */
  public PropsEntry getProfileProperty(final String profile, final String key) {
    final Map<String, PropsEntry> profileMap = profileProperties.get(profile);
    if (profileMap == null) {
      return null;
    }
    return profileMap.get(key);
  }


  // ---------------------------------------------------------------- lookup

  /**
   * Lookup props value through profiles and base properties.
   */
  protected String lookupValue(final String key, final String... profiles) {
    if (profiles != null) {
      for (String profile : profiles) {
        if (profile == null) {
          continue;
        }
        while (true) {
          final Map<String, PropsEntry> profileMap = this.profileProperties.get(profile);
          if (profileMap != null) {
            final PropsEntry value = profileMap.get(key);

            if (value != null) {
              return value.getValue(profiles);
            }
          }

          // go back with profile
          final int ndx = profile.lastIndexOf('.');
          if (ndx == -1) {
            break;
          }
          profile = profile.substring(0, ndx);
        }
      }
    }
    final PropsEntry value = getBaseProperty(key);

    if (value == null) {
      return null;
    }

    return value.getValue(profiles);
  }

  // ---------------------------------------------------------------- resolve

  /**
   * Resolves all macros in this props set. Called on property lookup.
   */
  public String resolveMacros(String value, final String... profiles) {
    // create string template parser that will be used internally
    StringTemplateParser stringTemplateParser = new StringTemplateParser();
    stringTemplateParser.setResolveEscapes(false);

    if (!ignoreMissingMacros) {
      stringTemplateParser.setReplaceMissingKey(false);
    } else {
      stringTemplateParser.setReplaceMissingKey(true);
      stringTemplateParser.setMissingKeyReplacement(StringPool.EMPTY);
    }

    final StringTemplateParser.MacroResolver macroResolver = new StringTemplateParser.MacroResolver() {
      public String resolve(String macroName) {
        String[] lookupProfiles = profiles;

        int leftIndex = macroName.indexOf('<');
        if (leftIndex != -1) {
          int rightIndex = macroName.indexOf('>');

          String profiles = macroName.substring(leftIndex + 1, rightIndex);
          macroName = macroName.substring(0, leftIndex).concat(macroName.substring(rightIndex + 1));

          lookupProfiles = StringUtil.splitc(profiles, ',');

          StringUtil.trimAll(lookupProfiles);
        }

        return lookupValue(macroName, lookupProfiles);
      }
    };

    // start parsing
    int loopCount = 0;

    while (loopCount++ < MAX_INNER_MACROS) {
      final String newValue = stringTemplateParser.parse(value, macroResolver);

      if (newValue.equals(value)) {
        break;
      }

      if (skipEmptyProps) {
        if (newValue.length() == 0) {
          return null;
        }
      }

      value = newValue;
    }

    return value;
  }

  // ---------------------------------------------------------------- extract

  /**
   * Extract props to target map.
   */
  public void extract(final Map target, final String[] profiles, final String[] wildcardPatterns) {
    if (profiles != null) {
      for (String profile : profiles) {
        while (true) {
          final Map<String, PropsEntry> map = this.profileProperties.get(profile);
          if (map != null) {
            extractMap(target, map, profiles, wildcardPatterns);
          }

          final int ndx = profile.indexOf('.');
          if (ndx == -1) {
            break;
          }
          profile = profile.substring(0, ndx);
        }
      }
    }
    extractMap(target, this.baseProperties, profiles, wildcardPatterns);
  }

  @SuppressWarnings("unchecked")
  protected void extractMap(final Map target, final Map<String, PropsEntry> map, final String[] profiles, final String[] wildcardPatterns) {
    for (Map.Entry<String, PropsEntry> entry : map.entrySet()) {
      final String key = entry.getKey();

      if (wildcardPatterns != null) {
        if (Wildcard.matchOne(key, wildcardPatterns) == -1) {
          continue;
        }
      }

      if (!target.containsKey(key)) {
        target.put(key, entry.getValue().getValue(profiles));
      }
    }
  }

}
TOP

Related Classes of jodd.props.PropsData

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.