All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cache2k.xmlConfiguration.StandardVariableExpander Maven / Gradle / Ivy

Go to download

A light weight and high performance Java caching library. XML configuration file support. This artifact is included in cache2k-all.

There is a newer version: 1.0.2.Final
Show newest version
package org.cache2k.xmlConfiguration;

/*
 * #%L
 * cache2k XML configuration
 * %%
 * Copyright (C) 2000 - 2017 headissue GmbH, Munich
 * %%
 * 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.
 * #L%
 */

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

/**
 * Lightweight and straight forward variable expansion.
 *
 * @author Jens Wilke
 */
@SuppressWarnings("WeakerAccess")
public class StandardVariableExpander implements VariableExpander {

  private Map scope2resolver = new HashMap();

  {
    scope2resolver.put("ENV", new ValueAccessor() {
      @Override
      public String get(final ExpanderContext ctx, final String _variable) {
        return System.getenv(_variable);
      }
    });
    scope2resolver.put("PROP", new ValueAccessor() {
      @Override
      public String get(ExpanderContext ctx, final String _variable) {
        return System.getProperty(_variable);
      }
    });
    scope2resolver.put("TOP", new ValueAccessor() {
      @Override
      public String get(ExpanderContext ctx, final String _variable) {
        ConfigurationTokenizer.Property p = ctx.getTopLevelConfiguration().getPropertyByPath(_variable);
        return checkAndReturnValue(p);
      }
    });
    scope2resolver.put("", new ValueAccessor() {
      @Override
      public String get(ExpanderContext ctx, final String _variable) {
        ConfigurationTokenizer.Property p = ctx.getCurrentConfiguration().getPropertyByPath(_variable);
        return checkAndReturnValue(p);
      }
    });

  }

  @Override
  public void expand(final ParsedConfiguration cfg) {
    new Process(cfg, new HashMap(scope2resolver)).expand();
  }

  private static class Process implements ExpanderContext {

    private final Map scope2resolver;
    private final ParsedConfiguration top;
    private ParsedConfiguration current;
    private int forwardReference = 0;
    private ConfigurationTokenizer.Property lastTroublemaker;

    Process(final ParsedConfiguration _top, final Map _scope2resolver) {
      top = _top;
      scope2resolver = _scope2resolver;
    }

    /**
     * Recurse into configuration objects and expand variables. It may happen that a property
     * refers to another property which is not yet expanded. In this case we repeat the expansion.
     * If the number of properties that are affected is not lowering per iteration there must
     * be a cyclic reference.
     */
    private void expand() {
      do {
        int lastCounter = forwardReference;
        forwardReference = 0;
        recurse(top);
        if (lastCounter > 0 && lastCounter == forwardReference) {
          throw new ConfigurationException("Cyclic reference", lastTroublemaker);
        }
      } while(forwardReference > 0);
    }

    private void recurse(ParsedConfiguration cfg) {
      current = cfg;
      for (ConfigurationTokenizer.Property p : cfg.getPropertyMap().values()) {
        if (p.isExpanded()) {
          continue;
        }
        String v0 = p.getValue();
        try {
          String v = expand(v0);
          if (v0 != v) {
            p.setValue(v);
          }
          p.setExpanded(true);
        } catch(NeedsExpansion ex) {
          forwardReference++;
          lastTroublemaker = p;
        }
      }
      for (ParsedConfiguration c2 : cfg.getSections()) {
        String _context = c2.getPropertyContext();
        ValueAccessor _savedAccessor = null;
        if (_context != null) {
          _savedAccessor = scope2resolver.get(_context);
          final ParsedConfiguration _localScope = c2;
          scope2resolver.put(_context, new ValueAccessor() {
            @Override
            public String get(final ExpanderContext ctx, final String _variable) {
              return _localScope.getStringPropertyByPath(_variable);
            }
          });
        }
        recurse(c2);
        if (_savedAccessor != null) {
          scope2resolver.put(_context, _savedAccessor);
        }
      }
    }

    @Override
    public ParsedConfiguration getCurrentConfiguration() {
      return current;
    }

    @Override
    public ParsedConfiguration getTopLevelConfiguration() {
      return top;
    }

    /**
     * Scan for sequences that look like a variable reference in the form of {@code ${scope.variablename}}
     * lookup and replace the variable with the expanded value.
     *
     * @return the identical string if nothing was expanded, a replacement string if something was expanded
     */
    private String expand(String s) {
      int idx = 0;
      int _endIdx;
      for (; ; idx = _endIdx) {
        idx = s.indexOf("${", idx);
        if (idx < 0) {
          return s;
        }
        _endIdx = s.indexOf('}', idx);
        if (_endIdx < 0) {
          return s;
        }
        int _scopeIdx = s.indexOf('.', idx);
        if (_scopeIdx < 0) {
          continue;
        }
        String _scope = s.substring(idx + 2, _scopeIdx);
        String _varName = s.substring(_scopeIdx + 1, _endIdx);
        ValueAccessor r = scope2resolver.get(_scope);
        if (r == null) {
          continue;
        }
        String _substitutionString = r.get(this, _varName);
        if (_substitutionString == null) {
          continue;
        }
        s = s.substring(0, idx) + _substitutionString + s.substring(_endIdx + 1);
        _endIdx = idx + _substitutionString.length();
      }
    }

  }

  private String checkAndReturnValue(final ConfigurationTokenizer.Property p) {
    if (p == null) { return null; }
    if (!p.isExpanded()) {
      throw new NeedsExpansion();
    }
    return p.getValue();
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy