/** * $Id: $ * $Date: $ * */ package org.xmlsh.core; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.xmlsh.sh.shell.SerializeOpts; import org.xmlsh.sh.shell.ShellConstants; import org.xmlsh.types.TypeFamily; import org.xmlsh.types.xtypes.IXValueContainer; import org.xmlsh.types.xtypes.IXValueList; import org.xmlsh.types.xtypes.IXValueMap; import org.xmlsh.types.xtypes.IXValueSequence; import org.xmlsh.types.xtypes.XValueList; import org.xmlsh.types.xtypes.XValueProperties; import org.xmlsh.types.xtypes.XValueProperty; import org.xmlsh.types.xtypes.XValueSequence; import org.xmlsh.util.NameValueMap; import org.xmlsh.util.StringPair; import org.xmlsh.util.Util; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonValue; /* * Universal/Generic configuration object * * Composed of XValueProperties in "sections" */ @JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE) public class XConfiguration implements IXValueContainer, IXValueMap { static Logger mLogger = LogManager.getLogger(); private NameValueMap<XValueProperties> mSections; private String mDefaultSectionName; /* * Default Configuration lookup strategy * */ public static class XConfigLookup extends XStringLookup { XConfiguration mConfig; public XConfigLookup(XConfiguration config, XStringLookup parent) { super(parent); mConfig = config; } /* * (non-Javadoc) * * @see org.xmlsh.core.XStringLookup#lookupXValue(java.lang.String) */ @Override protected XValue lookupXValue(String name) { try { return mConfig.get(name); } catch (InvalidArgumentException e) { mLogger.catching(e); } return null; } } public XConfiguration() { mSections = new NameValueMap<XValueProperties>(); } public XConfiguration(NameValueMap<XValueProperties> sections) { mSections = sections; } public XConfiguration(NameValueMap<XValueProperties> sections, String defName) { mSections = sections; mDefaultSectionName = defName; } public XConfiguration withSection(String section, XValueProperties props) { addSection(section, props); return this; } public XValueProperties getSection(String section) { return mSections.get(section); } public XValueProperties getSection(String section, boolean bCreate) { XValueProperties props = mSections.get(section); if(props == null && bCreate) mSections.put(section, props = new XValueProperties()); return props; } // Overrites section public void addSection(String section, XValueProperties properties) { mSections.put(section, properties); } // Merge properties on top of (replacing) all same-name values // return the merged properties public XValueProperties mergeSection(String section, XValueProperties properties) throws InvalidArgumentException { XValueProperties old = getSection(section); if(properties == null) return old; if(old != null) properties = old.merge(properties); mSections.put(section, properties); return properties; } // @JsonInclude // @JsonAnySetter public void setProperty(String section, XValueProperty prop) { getSection(section, true).put(prop); } public void setProperty(String section, String name, XValue value) { getSection(section, true).put(name, value); } public XValue getProperty(String section, String name, XValue defValue) { XValue value = getProperty(section, name); if(value == null) value = defValue; return value; } public XValue getProperty(String section, String name) { mLogger.entry(section, name); if(section == null) return null; XValueProperties sect = getSection(section); XValue value = null; if(sect != null) value = sect.getProperty(name); // default section if(value == null) { XValueProperties s = getDefaultSection(); if(s != null) value = s.getProperty(name); } return mLogger.exit(value); } @Override public boolean isEmpty() { return mSections.isEmpty(); } @Override public boolean isMap() { return true; } @Override public boolean isList() { return true; } @Override public boolean isAtomic() { return false; } @Override public boolean isContainer() { return true; } @Override public boolean isSequence() { return false; } @Override public void serialize(OutputStream out, SerializeOpts opts) throws IOException, InvalidArgumentException { try (OutputStreamWriter ps = new OutputStreamWriter(out, opts.getInputTextEncoding())) { for(Entry<String, XValueProperties> section : mSections.entrySet()) { ps.write(Util.getNewlineString()); ps.write("[" + section.getKey() + "]"); ps.flush(); XValueProperties prop = section.getValue(); prop.serialize(out, opts); } } } @Override public XValue asXValue() throws InvalidArgumentException { return XValue.newXValue(TypeFamily.XTYPE, this); } @Override public IXValueContainer asXContainer() { return this; } @Override public IXValueMap asXMap() { return this; } @Override public IXValueList asXList() { return new XValueList(values()); } @Override public IXValueSequence<? extends IXValueSequence<?>> asXSequence() { return new XValueSequence(); } @Override public int size() { return mSections.size(); } @Override public void removeAll() { mSections.clear(); } @Override public Set<String> keySet() { return mSections.keySet(); } @Override public Collection<XValue> values() { return Util.toList(iterator()); } @Override public Iterator<XValue> iterator() { return new Iterator<XValue>() { Iterator<XValueProperties> iter = mSections.values().iterator(); @Override public boolean hasNext() { return iter.hasNext(); } @Override public XValue next() { try { return iter.next().asXValue(); } catch (InvalidArgumentException e) { throw new IllegalArgumentException(e); } } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public XValue append(XValue item) { throw new UnsupportedOperationException(); } // Get property in "section.value" format or just "value" to use default // Formats // property // section // dotted.section // section.property // dotted.section.property // @JsonInclude // @JsonAnySetter @Override public XValue get(String name) throws InvalidArgumentException { assert (name != null); // Try a section first if(mSections.containsKey(name)) return mSections.get(name).asXValue(); // try a plain property if(name.indexOf(ShellConstants.kDOT_CHAR) < 0) return getProperty(mDefaultSectionName, name); // right to left find kDOT_CHAR // TODO handle [a.b] c.d=value , [a.b.c] d=value StringPair pair = new StringPair(name, ShellConstants.kDOT_CHAR, false); return getProperty(pair.getLeft(), pair.getRight()); } public XValue getProperty(String name) throws InvalidArgumentException { mLogger.entry(name); // 1) try a section first StringPair pair = new StringPair(name, ShellConstants.kDOT_CHAR); XValue value = null; // secion . name if(pair.hasLeft()) { value = getProperty(pair.getLeft(), pair.getRight()); } // default section if(value == null) { XValueProperties section = getDefaultSection(); if(section != null) value = section.getProperty(name); } return mLogger.exit(value); } @Override public XValue put(String key, XValue value) { if(value.isInstanceOf(XValueProperties.class)) addSection(key, value.asInstanceOf(XValueProperties.class)); else throw new UnsupportedOperationException(); return null; } @Override public boolean containsKey(String key) { // Simulate getting values ... could be key section.key including defaults if(mSections.containsKey(key)) return true; XValueProperties defs = getDefaultSection(); if(defs != null && defs.containsKey(key)) return true; // TODO handle [a.b] c.d=value , [a.b.c] d=value StringPair pair = new StringPair(key, ShellConstants.kDOT_CHAR, false); return pair.hasLeft() ? containsKey(pair.getLeft(), pair.getRight()) : false; } // Not sure this should allow missing sections with default values // e.g // "notexist" "name" public boolean containsKey(String sectname, String key) { XValueProperties sect = getSection(sectname); if(sect != null && sect.containsKey(key)) return true; XValueProperties defs = getDefaultSection(); if(defs != null && defs.containsKey(key)) return true; return false; } public void setDefaultSectionName(String defSection) { mDefaultSectionName = defSection; } public String getDefaultSectionName() { return mDefaultSectionName; } public XValueProperties getDefaultSection() { if(mDefaultSectionName != null) return getSection(mDefaultSectionName); return null; } // @JsonAnyGetter @JsonInclude @JsonValue public Map<String, XValueProperties> asMap() { return mSections; } public XConfiguration replace(XStringLookup parent) { XConfiguration that = new XConfiguration(); that.setDefaultSectionName(getDefaultSectionName()); XStringLookup lookup = getLookup(parent); for(Entry<String, XValueProperties> sect : mSections.entrySet()) { that.addSection(sect.getKey(), sect.getValue().replaceVariables(lookup)); } return that; } public XConfigLookup getLookup(XStringLookup parent) { return new XConfiguration.XConfigLookup(this, parent); } } /* * Copyright (C) 2008-2012 David A. Lee. * * The contents of this file are subject to the "Simplified BSD License" (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.opensource.org/licenses/bsd-license.php * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. * See the License for the specific language governing rights and limitations * under the License. * * The Original Code is: all this file. * * The Initial Developer of the Original Code is David A. Lee * * Portions created by (your name) are Copyright (C) (your legal entity). All * Rights Reserved. * * Contributor(s): David A. Lee */