http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlStrippedDocSerializer.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlStrippedDocSerializer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlStrippedDocSerializer.java new file mode 100755 index 0000000..a03c32c --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/HtmlStrippedDocSerializer.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.html; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.serializer.*; + +/** + * Serializes POJOs to HTTP responses as stripped HTML. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Accept</code> types: <code>text/html+stripped</code> + * <p> + * Produces <code>Content-Type</code> types: <code>text/html</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * Produces the same output as {@link HtmlDocSerializer}, but without the header and body tags and page title and description. + * Used primarily for JUnit testing the {@link HtmlDocSerializer} class. + * + * + * @author James Bognar ([email protected]) + */ +@Produces(value="text/html+stripped",contentType="text/html") +public class HtmlStrippedDocSerializer extends HtmlSerializer { + + //--------------------------------------------------------------------------- + // Overridden methods + //--------------------------------------------------------------------------- + + @Override /* Serializer */ + protected void doSerialize(Object o, Writer out, SerializerContext ctx) throws IOException, SerializeException { + HtmlSerializerContext hctx = (HtmlSerializerContext)ctx; + HtmlSerializerWriter w = hctx.getWriter(out); + if (o == null + || (o instanceof Collection && ((Collection<?>)o).size() == 0) + || (o.getClass().isArray() && Array.getLength(o) == 0)) + w.sTag(1, "p").append("No Results").eTag("p").nl(); + else + super.doSerialize(o, w, hctx); + } +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/SimpleHtmlWriter.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/SimpleHtmlWriter.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/SimpleHtmlWriter.class new file mode 100755 index 0000000..3d86ffb Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/SimpleHtmlWriter.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/SimpleHtmlWriter.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/SimpleHtmlWriter.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/SimpleHtmlWriter.java new file mode 100755 index 0000000..5c10459 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/SimpleHtmlWriter.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.html; + +import java.io.*; + +/** + * Utility class for creating custom HTML. + * <p> + * Example: + * <p class='bcode'> + * String table = <jk>new</jk> SimpleHtmlWriter().sTag(<js>"table"</js>).sTag(<js>"tr"</js>).sTag(<js>"td"</js>).append(<js>"hello"</js>).eTag(<js>"td"</js>).eTag(<js>"tr"</js>).eTag(<js>"table"</js>).toString(); + * </p> + * + * @author James Bognar ([email protected]) + */ +public class SimpleHtmlWriter extends HtmlSerializerWriter { + + /** + * Constructor. + */ + public SimpleHtmlWriter() { + super(new StringWriter(), true, '\'', null, null); + } + + @Override /* Object */ + public String toString() { + return out.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/Html.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/Html.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/Html.class new file mode 100755 index 0000000..0f0e970 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/Html.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/Html.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/Html.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/Html.java new file mode 100755 index 0000000..4d9829b --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/Html.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.html.annotation; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.*; + +import com.ibm.juno.core.html.*; + +/** + * Annotation that can be applied to classes, fields, and methods to tweak how + * they are handled by {@link HtmlSerializer}. + * + * @author James Bognar ([email protected]) + */ +@Documented +@Target({TYPE,FIELD,METHOD}) +@Retention(RUNTIME) +@Inherited +public @interface Html { + + /** + * Treat as XML. + * Useful when creating beans that model HTML elements. + */ + boolean asXml() default false; + + /** + * Treat as plain text. + * Object is serialized to a String using the <code>toString()</code> method and written directly to output. + * Useful when you want to serialize custom HTML. + */ + boolean asPlainText() default false; + + /** + * When <jk>true</jk>, collections of beans should be rendered as trees instead of tables. + * Default is <jk>false</jk>. + */ + boolean noTables() default false; + + /** + * When <jk>true</jk>, don't add headers to tables. + * Default is <jk>false</jk>. + */ + boolean noTableHeaders() default false; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/package.html ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/package.html b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/package.html new file mode 100755 index 0000000..082a694 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/annotation/package.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<!-- + Licensed Materials - Property of IBM + (c) Copyright IBM Corporation 2014. All Rights Reserved. + + Note to U.S. Government Users Restricted Rights: + Use, duplication or disclosure restricted by GSA ADP Schedule + Contract with IBM Corp. + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>HTML annotations</p> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_DESCRIPTION.png ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_DESCRIPTION.png b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_DESCRIPTION.png new file mode 100755 index 0000000..621721b Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_DESCRIPTION.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_LINKS.png ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_LINKS.png b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_LINKS.png new file mode 100755 index 0000000..3c07fe6 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_LINKS.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_TITLE.png ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_TITLE.png b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_TITLE.png new file mode 100755 index 0000000..5365735 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/doc-files/HTML_TITLE.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/HtmlElement.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/HtmlElement.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/HtmlElement.class new file mode 100755 index 0000000..4a7103e Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/HtmlElement.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/HtmlElement.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/HtmlElement.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/HtmlElement.java new file mode 100755 index 0000000..986e4e9 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/HtmlElement.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014. All Rights Reserved. + * + * Note to U.S. Government Users Restricted Rights: Use, + * duplication or disclosure restricted by GSA ADP Schedule + * Contract with IBM Corp. + *******************************************************************************/ +package com.ibm.juno.core.html.dto; + +import com.ibm.juno.core.html.*; +import com.ibm.juno.core.html.annotation.*; + +/** + * Superclass for all HTML elements. + * <p> + * These are beans that when serialized using {@link HtmlSerializer} generate + * valid XHTML elements. + * + * @author James Bognar ([email protected]) + */ +@Html(asXml=true) +public abstract class HtmlElement {} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/Img.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/Img.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/Img.class new file mode 100755 index 0000000..70226c9 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/Img.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/Img.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/Img.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/Img.java new file mode 100755 index 0000000..f8e1672 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/Img.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * Note to U.S. Government Users Restricted Rights: Use, + * duplication or disclosure restricted by GSA ADP Schedule + * Contract with IBM Corp. + *******************************************************************************/ +package com.ibm.juno.core.html.dto; + +import static com.ibm.juno.core.xml.annotation.XmlFormat.*; + +import com.ibm.juno.core.xml.annotation.*; + +/** + * Represents an HTML IMG element. + * + * @author James Bognar ([email protected]) + */ +@Xml(name="img") +public class Img extends HtmlElement { + + /** <code>src</code> attribute */ + @Xml(format=ATTR) + public String src; + + /** + * Constructor + * + * @param src <code>src</code> attribute + */ + public Img(String src) { + this.src = src; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/package.html ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/package.html b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/package.html new file mode 100755 index 0000000..d3e9ed5 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/dto/package.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<!-- + Licensed Materials - Property of IBM + (c) Copyright IBM Corporation 2014. All Rights Reserved. + + Note to U.S. Government Users Restricted Rights: + Use, duplication or disclosure restricted by GSA ADP Schedule + Contract with IBM Corp. + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>HTML Data Transfer Objects</p> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/package.html ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/package.html b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/package.html new file mode 100755 index 0000000..b06a2fc --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/html/package.html @@ -0,0 +1,72 @@ +<!DOCTYPE HTML> +<!-- + Licensed Materials - Property of IBM + (c) Copyright IBM Corporation 2014. All Rights Reserved. + + Note to U.S. Government Users Restricted Rights: + Use, duplication or disclosure restricted by GSA ADP Schedule + Contract with IBM Corp. + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>HTML serialization and parsing support</p> +<script> + function toggle(x) { + var div = x.nextSibling; + while (div != null && div.nodeType != 1) + div = div.nextSibling; + if (div != null) { + var d = div.style.display; + if (d == 'block' || d == '') { + div.style.display = 'none'; + x.className += " closed"; + } else { + div.style.display = 'block'; + x.className = x.className.replace(/(?:^|\s)closed(?!\S)/g , '' ); + } + } + } +</script> + +<a id='TOC'></a><h5 class='toc'>Table of Contents</h5> +<ol class='toc'> + <li><p><a class='doclink' href='#HtmlSerializer'>HTML serialization support</a></p> + <li><p><a class='doclink' href='#HtmlParser'>HTML parsing support</a></p> +</ol> + +<!-- ======================================================================================================== --> +<a id="HtmlSerializer"></a> +<h2 class='topic' onclick='toggle(this)'>1 - HTML serialization support</h2> +<div class='topic'> + TODO +</div> + +<!-- ======================================================================================================== --> +<a id="HtmlParser"></a> +<h2 class='topic' onclick='toggle(this)'>2 - HTML parsing support</h2> +<div class='topic'> + TODO +</div> + +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFile.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFile.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFile.class new file mode 100755 index 0000000..fbb891c Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFile.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFile.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFile.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFile.java new file mode 100755 index 0000000..4280133 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFile.java @@ -0,0 +1,743 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.ini; + +import static com.ibm.juno.core.ini.ConfigFileFormat.*; +import static com.ibm.juno.core.ini.ConfigUtils.*; +import static com.ibm.juno.core.utils.ThrowableUtils.*; +import static java.lang.reflect.Modifier.*; + +import java.beans.*; +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.json.*; +import com.ibm.juno.core.parser.*; +import com.ibm.juno.core.serializer.*; +import com.ibm.juno.core.utils.*; + +/** + * Implements the API for accessing the contents of a config file. + * <p> + * Refer to {@link com.ibm.juno.core.ini} for more information. + * + * @author James Bognar ([email protected]) + */ +public abstract class ConfigFile implements Map<String,Section> { + + //-------------------------------------------------------------------------------- + // Abstract methods + //-------------------------------------------------------------------------------- + + /** + * Retrieves an entry value from this config file. + * + * @param sectionName - The section name. Must not be <jk>null</jk>. + * @param sectionKey - The section key. Must not be <jk>null</jk>. + * @return The value, or the default value if the section or value doesn't exist. + */ + public abstract String get(String sectionName, String sectionKey); + + /** + * Sets an entry value in this config file. + * + * @param sectionName - The section name. Must not be <jk>null</jk>. + * @param sectionKey - The section key. Must not be <jk>null</jk>. + * @param value The new value. + * @param encoded + * @return The previous value, or <jk>null</jk> if the section or key did not previously exist. + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract String put(String sectionName, String sectionKey, Object value, boolean encoded); + + /** + * Removes an antry from this config file. + * + * @param sectionName - The section name. Must not be <jk>null</jk>. + * @param sectionKey - The section key. Must not be <jk>null</jk>. + * @return The previous value, or <jk>null</jk> if the section or key did not previously exist. + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract String remove(String sectionName, String sectionKey); + + /** + * Returns the current set of keys in the specified section. + * + * @param sectionName - The section name. Must not be <jk>null</jk>. + * @return The list of keys in the specified section, or <jk>null</jk> if section does not exist. + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract Set<String> getSectionKeys(String sectionName); + + /** + * Reloads ths config file object from the persisted file contents if the modified timestamp on the file has changed. + * + * @return This object (for method chaining). + * @throws IOException If file could not be read, or file is not associated with this object. + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile loadIfModified() throws IOException; + + /** + * Loads ths config file object from the persisted file contents. + * + * @return This object (for method chaining). + * @throws IOException If file could not be read, or file is not associated with this object. + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile load() throws IOException; + + /** + * Loads ths config file object from the specified reader. + * + * @param r The reader to read from. + * @return This object (for method chaining). + * @throws IOException If file could not be read, or file is not associated with this object. + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile load(Reader r) throws IOException; + + /** + * Adds arbitrary lines to the specified config file section. + * <p> + * The lines can be any of the following.... + * <ul> + * <li><js>"# comment"</js> - A comment line. + * <li><js>"key=val"</js> - A key/value pair (equivalent to calling {@link #put(String,Object)}. + * <li><js>" foobar "</js> - Anything else (interpreted as a comment). + * </ul> + * <p> + * If the section does not exist, it will automatically be created. + * + * @param section The name of the section to add lines to, or <jk>null</jk> to add to the beginning unnamed section. + * @param lines The lines to add to the section. + * @return This object (for method chaining). + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile addLines(String section, String...lines); + + /** + * Adds header comments to the specified section. + * <p> + * Header comments are defined as lines that start with <jk>"#"</jk> immediately preceding a section header <jk>"[section]"</jk>. + * These are handled as part of the section itself instead of being interpreted as comments in the previous section. + * <p> + * Header comments can be of the following formats... + * <ul> + * <li><js>"# comment"</js> - A comment line. + * <li><js>"comment"</js> - Anything else (will automatically be prefixed with <js>"# "</js>). + * </ul> + * <p> + * If the section does not exist, it will automatically be created. + * + * @param section The name of the section to add lines to, or <jk>null</jk> to add to the default section. + * @param headerComments The comment lines to add to the section. + * @return This object (for method chaining). + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile addHeaderComments(String section, String...headerComments); + + /** + * Removes any header comments from the specified section. + * + * @param section The name of the section to remove header comments from. + * @return This object (for method chaining). + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile clearHeaderComments(String section); + + /** + * Returns the serializer in use for this config file. + * + * @return This object (for method chaining). + * @throws SerializeException If no serializer is defined on this config file. + */ + protected abstract WriterSerializer getSerializer() throws SerializeException; + + /** + * Returns the parser in use for this config file. + * + * @return This object (for method chaining). + * @throws ParseException If no parser is defined on this config file. + */ + protected abstract ReaderParser getParser() throws ParseException; + + /** + * Places a read lock on this config file. + */ + protected abstract void readLock(); + + /** + * Removes the read lock on this config file. + */ + protected abstract void readUnlock(); + + + //-------------------------------------------------------------------------------- + // API methods + //-------------------------------------------------------------------------------- + + /** + * Returns the specified value as a string from the config file. + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @param def The default value if the section or value does not exist. + * @return The value, or the default value if the section or value doesn't exist. + */ + public final String getString(String key, String def) { + assertFieldNotNull(key, "key"); + String s = get(getSectionName(key), getSectionKey(key)); + return (s == null ? def : s); + } + + /** + * Removes an entry with the specified key. + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @return The previous value, or <jk>null</jk> if the section or key did not previously exist. + * @throws UnsupportedOperationException If config file is read only. + */ + public final String removeString(String key) { + assertFieldNotNull(key, "key"); + return remove(getSectionName(key), getSectionKey(key)); + } + + /** + * Gets the entry with the specified key and converts it to the specified value. + * <p> + * The key can be in one of the following formats... + * <ul> + * <li><js>"key"</js> - A value in the default section (i.e. defined above any <code>[section]</code> header). + * <li><js>"section/key"</js> - A value from the specified section. + * </ul> + * <p> + * If the class type is an array, the value is split on commas and converted individually. + * <p> + * If you specify a primitive element type using this method (e.g. <code><jk>int</jk>.<jk>class</jk></code>, + * you will get an array of wrapped objects (e.g. <code>Integer[].<jk>class</jk></code>. + * + * @param c The class to convert the value to. + * @param key The key. See {@link #getString(String)} for a description of the key. + * @throws ParseException If parser could not parse the value or if a parser is not registered with this config file. + * @return The value, or <jk>null</jk> if the section or key does not exist. + */ + @SuppressWarnings("unchecked") + public final <T> T getObject(Class<T> c, String key) throws ParseException { + assertFieldNotNull(c, "c"); + return getObject(c, key, c.isArray() ? (T)Array.newInstance(c.getComponentType(), 0) : null); + } + + /** + * Gets the entry with the specified key and converts it to the specified value.. + * <p> + * The key can be in one of the following formats... + * <ul> + * <li><js>"key"</js> - A value in the default section (i.e. defined above any <code>[section]</code> header). + * <li><js>"section/key"</js> - A value from the specified section. + * </ul> + * + * @param c The class to convert the value to. + * @param key The key. See {@link #getString(String)} for a description of the key. + * @param def The default value if section or key does not exist. + * @throws ParseException If parser could not parse the value or if a parser is not registered with this config file. + * @return The value, or <jk>null</jk> if the section or key does not exist. + */ + public final <T> T getObject(Class<T> c, String key, T def) throws ParseException { + assertFieldNotNull(c, "c"); + assertFieldNotNull(key, "key"); + return getObject(c, getSectionName(key), getSectionKey(key), def); + } + + /** + * Same as {@link #getObject(Class, String, Object)}, but value is referenced through section name and key instead of full key. + * + * @param c The class to convert the value to. + * @param sectionName - The section name. Must not be <jk>null</jk>. + * @param sectionKey - The section key. Must not be <jk>null</jk>. + * @param def The default value if section or key does not exist. + * @throws ParseException If parser could not parse the value or if a parser is not registered with this config file. + * @return The value, or the default value if the section or value doesn't exist. + */ + @SuppressWarnings("unchecked") + public <T> T getObject(Class<T> c, String sectionName, String sectionKey, T def) throws ParseException { + String s = get(sectionName, sectionKey); + if (s == null) + return def; + if (c == String.class) + return (T)s; + if (c == Integer.class || c == int.class) + return (T)(StringUtils.isEmpty(s) ? def : Integer.valueOf(parseIntWithSuffix(s))); + if (c == Boolean.class || c == boolean.class) + return (T)(StringUtils.isEmpty(s) ? def : Boolean.valueOf(Boolean.parseBoolean(s))); + if (c == String[].class) { + String[] r = StringUtils.isEmpty(s) ? new String[0] : StringUtils.split(s, ','); + return (T)(r.length == 0 ? def : r); + } + if (c.isArray()) { + Class<?> ce = c.getComponentType(); + if (StringUtils.isEmpty(s)) + return def; + String[] r = StringUtils.split(s, ','); + Object o = Array.newInstance(ce, r.length); + for (int i = 0; i < r.length; i++) + Array.set(o, i, getParser().parse(r[i], ce)); + return (T)o; + } + if (StringUtils.isEmpty(s)) + return def; + return getParser().parse(s, c); + } + + /** + * Gets the entry with the specified key. + * <p> + * The key can be in one of the following formats... + * <ul> + * <li><js>"key"</js> - A value in the default section (i.e. defined above any <code>[section]</code> header). + * <li><js>"section/key"</js> - A value from the specified section. + * </ul> + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @return The value, or <jk>null</jk> if the section or key does not exist. + */ + public final String getString(String key) { + return getString(key, null); + } + + /** + * Gets the entry with the specified key, splits the value on commas, and returns the values as trimmed strings. + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @return The value, or an empty list if the section or key does not exist. + */ + public final String[] getStringArray(String key) { + return getStringArray(key, new String[0]); + } + + /** + * Same as {@link #getStringArray(String)} but returns a default value if the value cannot be found. + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @param def The default value if section or key does not exist. + * @return The value, or an empty list if the section or key does not exist. + */ + public final String[] getStringArray(String key, String[] def) { + String s = getString(key); + if (s == null) + return def; + String[] r = StringUtils.isEmpty(s) ? new String[0] : StringUtils.split(s, ','); + return r.length == 0 ? def : r; + } + + /** + * Convenience method for getting int config values. + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @return The value, or <code>0</code> if the section or key does not exist or cannot be parsed as an integer. + */ + public final int getInt(String key) { + return getInt(key, 0); + } + + /** + * Convenience method for getting int config values. + * <p> + * <js>"M"</js> and <js>"K"</js> can be used to identify millions and thousands. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <ul> + * <li><code><js>"100K"</js> => 1024000</code> + * <li><code><js>"100M"</js> => 104857600</code> + * </ul> + * </dd> + * </dl> + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @param def The default value if config file or value does not exist. + * @return The value, or the default value if the section or key does not exist or cannot be parsed as an integer. + */ + public final int getInt(String key, int def) { + String s = getString(key); + if (StringUtils.isEmpty(s)) + return def; + return parseIntWithSuffix(s); + } + + /** + * Convenience method for getting boolean config values. + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @return The value, or <jk>false</jk> if the section or key does not exist or cannot be parsed as a boolean. + */ + public final boolean getBoolean(String key) { + return getBoolean(key, false); + } + + /** + * Convenience method for getting boolean config values. + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @param def The default value if config file or value does not exist. + * @return The value, or the default value if the section or key does not exist or cannot be parsed as a boolean. + */ + public final boolean getBoolean(String key, boolean def) { + String s = getString(key); + return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s); + } + + /** + * Adds or replaces an entry with the specified key with a POJO serialized to a string using the registered serializer. + * <p> + * Equivalent to calling <code>put(key, value, isEncoded(key))</code>. + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @param value The new value POJO. + * @return The previous value, or <jk>null</jk> if the section or key did not previously exist. + * @throws SerializeException If serializer could not serialize the value or if a serializer is not registered with this config file. + * @throws UnsupportedOperationException If config file is read only. + */ + public final String put(String key, Object value) throws SerializeException { + return put(key, value, isEncoded(key)); + } + + /** + * Adds or replaces an entry with the specified key with the specified value. + * <p> + * The format of the entry depends on the data type of the value. + * <ul> + * <li>Simple types (<code>String</code>, <code>Number</code>, <code>Boolean</code>, primitives) + * are serialized as plain strings. + * <li>Arrays and collections of simple types are serialized as comma-delimited lists of plain strings. + * <li>Other types (e.g. beans) are serialized using the serializer registered with this config file. + * <li>Arrays and collections of other types are serialized as comma-delimited lists of serialized strings of each entry. + * </ul> + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @param value The new value. + * @param encoded If <jk>true</jk>, value is encoded by the registered encoder when the config file is persisted to disk. + * @return The previous value, or <jk>null</jk> if the section or key did not previously exist. + * @throws SerializeException If serializer could not serialize the value or if a serializer is not registered with this config file. + * @throws UnsupportedOperationException If config file is read only. + */ + public final String put(String key, Object value, boolean encoded) throws SerializeException { + assertFieldNotNull(key, "key"); + if (value == null) + value = ""; + Class<?> c = value.getClass(); + if (isSimpleType(c)) + return put(getSectionName(key), getSectionKey(key), value.toString(), encoded); + if (c.isAssignableFrom(Collection.class)) { + Collection<?> c2 = (Collection<?>)value; + String[] r = new String[c2.size()]; + int i = 0; + for (Object o2 : c2) { + boolean isSimpleType = o2 == null ? true : isSimpleType(o2.getClass()); + r[i++] = (isSimpleType ? Array.get(value, i).toString() : getSerializer().toString(Array.get(value, i))); + } + return put(getSectionName(key), getSectionKey(key), StringUtils.join(r, ','), encoded); + } else if (c.isArray()) { + boolean isSimpleType = isSimpleType(c.getComponentType()); + String[] r = new String[Array.getLength(value)]; + for (int i = 0; i < r.length; i++) { + r[i] = (isSimpleType ? Array.get(value, i).toString() : getSerializer().toString(Array.get(value, i))); + } + return put(getSectionName(key), getSectionKey(key), StringUtils.join(r, ','), encoded); + } + return put(getSectionName(key), getSectionKey(key), getSerializer().toString(value), encoded); + } + + private final boolean isSimpleType(Class<?> c) { + return (c == String.class || c.isPrimitive() || c.isAssignableFrom(Number.class) || c == Boolean.class); + } + + /** + * Returns the specified section as a map of key/value pairs. + * + * @param sectionName The section name to retrieve. + * @return A map of the section, or <jk>null</jk> if the section was not found. + */ + public final ObjectMap getSectionMap(String sectionName) { + readLock(); + try { + Set<String> keys = getSectionKeys(sectionName); + if (keys == null) + return null; + ObjectMap m = new ObjectMap(); + for (String key : keys) + m.put(key, get(sectionName, key)); + return m; + } finally { + readUnlock(); + } + } + + /** + * Copies the entries in a section to the specified bean by calling the public setters on that bean. + * + * @param sectionName The section name to write from. + * @param bean The bean to set the properties on. + * @param ignoreUnknownProperties If <jk>true</jk>, don't throw an {@link IllegalArgumentException} if this section + * contains a key that doesn't correspond to a setter method. + * @param permittedPropertyTypes If specified, only look for setters whose property types + * are those listed. If not specified, use all setters. + * @return An object map of the changes made to the bean. + * @throws ParseException If parser was not set on this config file or invalid properties were found in the section. + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public final ObjectMap writeProperties(String sectionName, Object bean, boolean ignoreUnknownProperties, Class<?>...permittedPropertyTypes) throws ParseException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { + assertFieldNotNull(bean, "bean"); + ObjectMap om = new ObjectMap(); + readLock(); + try { + Set<String> keys = getSectionKeys(sectionName); + if (keys == null) + throw new IllegalArgumentException("Section not found"); + keys = new LinkedHashSet<String>(keys); + for (Method m : bean.getClass().getMethods()) { + int mod = m.getModifiers(); + if (isPublic(mod) && (!isStatic(mod)) && m.getName().startsWith("set") && m.getParameterTypes().length == 1) { + Class<?> pt = m.getParameterTypes()[0]; + if (permittedPropertyTypes == null || permittedPropertyTypes.length == 0 || ArrayUtils.contains(pt, permittedPropertyTypes)) { + String propName = Introspector.decapitalize(m.getName().substring(3)); + Object value = getObject(pt, sectionName, propName, null); + if (value != null) { + m.invoke(bean, value); + om.put(propName, value); + keys.remove(propName); + } + } + } + } + if (! (ignoreUnknownProperties || keys.isEmpty())) + throw new ParseException("Invalid properties found in config file section ["+sectionName+"]: " + JsonSerializer.DEFAULT_LAX.toString(keys)); + return om; + } finally { + readUnlock(); + } + } + + /** + * Shortcut for calling <code>asBean(sectionName, c, <jk>false</jk>)</code>. + * + * @param sectionName The section name to write from. + * @param c The bean class to create. + * @return A new bean instance. + * @throws ParseException + */ + public final <T> T getSectionAsBean(String sectionName, Class<T>c) throws ParseException { + return getSectionAsBean(sectionName, c, false); + } + + /** + * Converts this config file section to the specified bean instance. + * + * @param sectionName The section name to write from. + * @param c The bean class to create. + * @param ignoreUnknownProperties If <jk>false</jk>, throws a {@link ParseException} if + * the section contains an entry that isn't a bean property name. + * @return A new bean instance. + * @throws ParseException + */ + public final <T> T getSectionAsBean(String sectionName, Class<T> c, boolean ignoreUnknownProperties) throws ParseException { + assertFieldNotNull(c, "c"); + readLock(); + try { + BeanMap<T> bm = getParser().getBeanContext().newBeanMap(c); + for (String k : getSectionKeys(sectionName)) { + BeanPropertyMeta<?> bpm = bm.getPropertyMeta(k); + if (bpm == null) { + if (! ignoreUnknownProperties) + throw new ParseException("Unknown property {0} encountered", k); + } else { + bm.put(k, getObject(bpm.getClassMeta().getInnerClass(), sectionName + '/' + k)); + } + } + return bm.getBean(); + } finally { + readUnlock(); + } + } + + /** + * Returns <jk>true</jk> if this section contains the specified key and the key has a non-blank value. + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @return <jk>true</jk> if this section contains the specified key and the key has a non-blank value. + */ + public final boolean containsNonEmptyValue(String key) { + return ! StringUtils.isEmpty(getString(key, null)); + } + + /** + * Gets the section with the specified name. + * + * @param name The section name. + * @return The section, or <jk>null</jk> if section does not exist. + */ + protected abstract Section getSection(String name); + + /** + * Gets the section with the specified name and optionally creates it if it's not there. + * + * @param name The section name. + * @param create Create the section if it's not there. + * @return The section, or <jk>null</jk> if section does not exist. + * @throws UnsupportedOperationException If config file is read only and section doesn't exist and <code>create</code> is <jk>true</jk>. + */ + protected abstract Section getSection(String name, boolean create); + + /** + * Appends a section to this config file if it does not already exist. + * <p> + * Returns the existing section if it already exists. + * + * @param name The section name, or <jk>null</jk> for the default section. + * @return The appended or existing section. + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile addSection(String name); + + /** + * Creates or overwrites the specified section. + * + * @param name The section name, or <jk>null</jk> for the default section. + * @param contents The contents of the new section. + * @return The appended or existing section. + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile setSection(String name, Map<String,String> contents); + + /** + * Removes the section with the specified name. + * + * @param name The name of the section to remove, or <jk>null</jk> for the default section. + * @return The removed section, or <jk>null</jk> if named section does not exist. + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile removeSection(String name); + + /** + * Returns <jk>true</jk> if the encoding flag is set on the specified entry. + * + * @param key The key. See {@link #getString(String)} for a description of the key. + * @return <jk>true</jk> if the encoding flag is set on the specified entry. + */ + public abstract boolean isEncoded(String key); + + /** + * Saves this config file to disk. + * + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to save file to disk, or file is not associated with this object. + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile save() throws IOException; + + /** + * Saves this config file to the specified writer as an INI file. + * <p> + * The writer will automatically be closed. + * + * @param out The writer to send the output to. + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to send contents to the writer. + */ + public final ConfigFile serializeTo(Writer out) throws IOException { + return serializeTo(out, INI); + } + + /** + * Same as {@link #serializeTo(Writer)}, except allows you to explicitely specify a format. + * + * @param out The writer to send the output to. + * @param format The {@link ConfigFileFormat} of the output. + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to send contents to the writer. + */ + public abstract ConfigFile serializeTo(Writer out, ConfigFileFormat format) throws IOException; + + /** + * Add a listener to this config file to react to modification events. + * + * @param listener The new listener to add. + * @return This object (for method chaining). + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile addListener(ConfigFileListener listener); + + /** + * Merges the contents of the specified config file into this config file. + * <p> + * Pretty much identical to just replacing this config file, but + * causes the {@link ConfigFileListener#onChange(ConfigFile, Set)} method to be invoked + * on differences between the file. + * @param cf The config file whose values should be copied into this config file. + * @return This object (for method chaining). + * @throws UnsupportedOperationException If config file is read only. + */ + public abstract ConfigFile merge(ConfigFile cf); + + /** + * Returns the config file contents as a string. + * <p> + * The contents of the string are the same as the contents that would be serialized to disk. + */ + @Override /* Object */ + public abstract String toString(); + + /** + * Returns a wrapped instance of this config file where calls to getters + * have their values first resolved by the specified {@link StringVarResolver}. + * <p> + * @param vr + * @return This config file wrapped in an instance of {@link ConfigFileWrapped}. + */ + public abstract ConfigFile getResolving(StringVarResolver vr); + + /** + * Returns a wrapped instance of this config file where calls to getters have their values + * first resolved by a default {@link StringVarResolver}. + * + * The default {@link StringVarResolver} is registered with the following {@link StringVar StringVars}: + * <ul> + * <li><code>$S{key}</code>,<code>$S{key,default}</code> - System properties. + * <li><code>$E{key}</code>,<code>$E{key,default}</code> - Environment variables. + * <li><code>$C{key}</code>,<code>$C{key,default}</code> - Values in this configuration file. + * </ul> + * + * @return A new config file that resolves string variables. + */ + public abstract ConfigFile getResolving(); + + /** + * Wraps this config file in a {@link Writable} interface that renders it as plain text. + * + * @return This config file wrapped in a {@link Writable}. + */ + public abstract Writable toWritable(); + + private int parseIntWithSuffix(String s) { + assertFieldNotNull(s, "s"); + int m = 1; + if (s.endsWith("M")) { + m = 1024*1024; + s = s.substring(0, s.length()-1).trim(); + } else if (s.endsWith("K")) { + m = 1024; + s = s.substring(0, s.length()-1).trim(); + } + return Integer.parseInt(s) * m; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileFormat.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileFormat.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileFormat.class new file mode 100755 index 0000000..2af9062 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileFormat.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileFormat.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileFormat.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileFormat.java new file mode 100755 index 0000000..ae991e7 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileFormat.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.ini; + +import java.io.*; + +/** + * Valid formats that can be passed to the {@link ConfigFile#serializeTo(Writer, ConfigFileFormat)} method. + */ +public enum ConfigFileFormat { + /** Normal INI file format*/ + INI, + + /** Batch file with "set X" commands */ + BATCH, + + /** Shell script file with "export X" commands */ + SHELL; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$1$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$1$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$1$1.class new file mode 100755 index 0000000..df4b978 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$1$1.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$1.class new file mode 100755 index 0000000..248452a Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$1.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$2$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$2$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$2$1.class new file mode 100755 index 0000000..9dcb412 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$2$1.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$2.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$2.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$2.class new file mode 100755 index 0000000..a533afa Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$2.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$3$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$3$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$3$1.class new file mode 100755 index 0000000..b09b475 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$3$1.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$3.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$3.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$3.class new file mode 100755 index 0000000..d132e6a Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$3.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$4.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$4.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$4.class new file mode 100755 index 0000000..3f9c85a Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl$4.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl.class new file mode 100755 index 0000000..55cd0f5 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl.java new file mode 100755 index 0000000..e9a0a64 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileImpl.java @@ -0,0 +1,729 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.ini; + +import static com.ibm.juno.core.ini.ConfigUtils.*; +import static com.ibm.juno.core.utils.ThrowableUtils.*; + +import java.io.*; +import java.nio.charset.*; +import java.util.*; +import java.util.concurrent.locks.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.json.*; +import com.ibm.juno.core.parser.*; +import com.ibm.juno.core.serializer.*; +import com.ibm.juno.core.utils.*; + +/** + * Implementation class for {@link ConfigFile}. + * + * @author James Bognar ([email protected]) + */ +public final class ConfigFileImpl extends ConfigFile { + + private final File file; + private final Encoder encoder; + private final WriterSerializer serializer; + private final ReaderParser parser; + private final Charset charset; + final List<ConfigFileListener> listeners = Collections.synchronizedList(new ArrayList<ConfigFileListener>()); + + private Map<String,Section> sections; // The actual data. + + private static final String DEFAULT = "default"; + + private final boolean readOnly; + + volatile boolean hasBeenModified = false; + private ReadWriteLock lock = new ReentrantReadWriteLock(); + + long modifiedTimestamp; + + /** + * Constructor. + * <p> + * Loads the contents of the specified file into this config file. + * <p> + * If file does not initially exist, this object will start off empty. + * + * @param file The INI file on disk. + * If <jk>null</jk>, create an in-memory config file. + * @param readOnly Make this configuration file read-only. + * Attempting to set any values on this config file will cause {@link UnsupportedOperationException} to be thrown. + * @param encoder The encoder to use for encoding sensitive values in this configuration file. + * If <jk>null</jk>, defaults to {@link XorEncoder#INSTANCE}. + * @param serializer The serializer to use for serializing POJOs in the {@link #put(String, Object)} method. + * If <jk>null</jk>, defaults to {@link JsonSerializer#DEFAULT}. + * @param parser The parser to use for parsing POJOs in the {@link #getObject(Class,String)} method. + * If <jk>null</jk>, defaults to {@link JsonParser#DEFAULT}. + * @param charset The charset on the files. + * If <jk>null</jk>, defaults to {@link Charset#defaultCharset()}. + * @throws IOException + */ + public ConfigFileImpl(File file, boolean readOnly, Encoder encoder, WriterSerializer serializer, ReaderParser parser, Charset charset) throws IOException { + this.file = file; + this.encoder = encoder == null ? XorEncoder.INSTANCE : encoder; + this.serializer = serializer == null ? JsonSerializer.DEFAULT : serializer; + this.parser = parser == null ? JsonParser.DEFAULT : parser; + this.charset = charset == null ? Charset.defaultCharset() : charset; + load(); + this.readOnly = readOnly; + if (readOnly) { + this.sections = Collections.unmodifiableMap(this.sections); + for (Section s : sections.values()) + s.setReadOnly(); + } + } + + /** + * Constructor. + * Shortcut for calling <code><jk>new</jk> ConfigFileImpl(file, <jk>false</jk>, <jk>null</jk>, <jk>null</jk>, <jk>null</jk>, <jk>null</jk>);</code> + * + * @param file The config file. Does not need to exist. + * @throws IOException + */ + public ConfigFileImpl(File file) throws IOException { + this(file, false, null, null, null, null); + } + + /** + * Constructor. + * Shortcut for calling <code><jk>new</jk> ConfigFileImpl(<jk>null</jk>, <jk>false</jk>, <jk>null</jk>, <jk>null</jk>, <jk>null</jk>, <jk>null</jk>);</code> + * + * @throws IOException + */ + public ConfigFileImpl() throws IOException { + this(null); + } + + @Override /* ConfigFile */ + public ConfigFileImpl loadIfModified() throws IOException { + if (file == null) + return this; + writeLock(); + try { + if (file.lastModified() > modifiedTimestamp) + load(); + } finally { + writeUnlock(); + } + return this; + } + + @Override /* ConfigFile */ + public ConfigFileImpl load() throws IOException { + Reader r = null; + if (file != null && file.exists()) + r = new InputStreamReader(new FileInputStream(file), charset); + else + r = new StringReader(""); + try { + load(r); + } finally { + r.close(); + } + return this; + } + + @Override /* ConfigFile */ + public ConfigFileImpl load(Reader r) throws IOException { + assertFieldNotNull(r, "r"); + writeLock(); + try { + this.sections = Collections.synchronizedMap(new LinkedHashMap<String,Section>()); + BufferedReader in = new BufferedReader(r); + try { + writeLock(); + hasBeenModified = false; + try { + sections.clear(); + String line = null; + Section section = getSection(null, true); + while ((line = in.readLine()) != null) { + if (line.matches("\\s*\\[.*\\].*")) { + String sn = line.substring(line.indexOf('[')+1, line.indexOf(']')).trim(); + section = getSection(sn, true).addHeaderComments(section.removeTrailingComments()); + } else { + section.addLines(null, line); + } + } + in.close(); + if (hasBeenModified) // Set when values need to be encoded. + save(); + if (file != null) + modifiedTimestamp = file.lastModified(); + } finally { + writeUnlock(); + } + } finally { + in.close(); + } + } finally { + writeUnlock(); + } + for (ConfigFileListener l : listeners) + l.onLoad(this); + return this; + } + + //-------------------------------------------------------------------------------- + // Map methods + //-------------------------------------------------------------------------------- + + @Override /* Map */ + public Section get(Object key) { + if (StringUtils.isEmpty(key)) + key = DEFAULT; + readLock(); + try { + return sections.get(key); + } finally { + readUnlock(); + } + } + + @Override /* Map */ + public Section put(String key, Section section) { + Set<String> changes = createChanges(); + Section old = put(key, section, changes); + signalChanges(changes); + return old; + } + + private Section put(String key, Section section, Set<String> changes) { + if (StringUtils.isEmpty(key)) + key = DEFAULT; + writeLock(); + try { + Section prev = sections.put(key, section); + findChanges(changes, prev, section); + return prev; + } finally { + writeUnlock(); + } + } + + @Override /* Map */ + public void putAll(Map<? extends String,? extends Section> map) { + Set<String> changes = createChanges(); + writeLock(); + try { + for (Map.Entry<? extends String,? extends Section> e : map.entrySet()) + put(e.getKey(), e.getValue(), changes); + } finally { + writeUnlock(); + } + signalChanges(changes); + } + + @Override /* Map */ + public void clear() { + Set<String> changes = createChanges(); + writeLock(); + try { + for (Section s : values()) + findChanges(changes, s, null); + sections.clear(); + } finally { + writeUnlock(); + } + signalChanges(changes); + } + + @Override /* Map */ + public boolean containsKey(Object key) { + if (StringUtils.isEmpty(key)) + key = DEFAULT; + return sections.containsKey(key); + } + + @Override /* Map */ + public boolean containsValue(Object value) { + return sections.containsValue(value); + } + + @Override /* Map */ + public Set<Map.Entry<String,Section>> entrySet() { + + // We need to create our own set so that entries are removed correctly. + return new AbstractSet<Map.Entry<String,Section>>() { + @Override /* Map */ + public Iterator<Map.Entry<String,Section>> iterator() { + return new Iterator<Map.Entry<String,Section>>() { + Iterator<Map.Entry<String,Section>> i = sections.entrySet().iterator(); + Map.Entry<String,Section> i2; + + @Override /* Iterator */ + public boolean hasNext() { + return i.hasNext(); + } + + @Override /* Iterator */ + public Map.Entry<String,Section> next() { + i2 = i.next(); + return i2; + } + + @Override /* Iterator */ + public void remove() { + Set<String> changes = createChanges(); + findChanges(changes, i2.getValue(), null); + i.remove(); + signalChanges(changes); + } + }; + } + + @Override /* Map */ + public int size() { + return sections.size(); + } + }; + } + + @Override /* Map */ + public boolean isEmpty() { + return sections.isEmpty(); + } + + @Override /* Map */ + public Set<String> keySet() { + + // We need to create our own set so that sections are removed correctly. + return new AbstractSet<String>() { + @Override /* Set */ + public Iterator<String> iterator() { + return new Iterator<String>() { + Iterator<String> i = sections.keySet().iterator(); + String i2; + + @Override /* Iterator */ + public boolean hasNext() { + return i.hasNext(); + } + + @Override /* Iterator */ + public String next() { + i2 = i.next(); + return i2; + } + + @Override /* Iterator */ + public void remove() { + Set<String> changes = createChanges(); + findChanges(changes, sections.get(i2), null); + i.remove(); + signalChanges(changes); + } + }; + } + + @Override /* Set */ + public int size() { + return sections.size(); + } + }; + } + + @Override /* Map */ + public int size() { + return sections.size(); + } + + @Override /* Map */ + public Collection<Section> values() { + return new AbstractCollection<Section>() { + @Override /* Collection */ + public Iterator<Section> iterator() { + return new Iterator<Section>() { + Iterator<Section> i = sections.values().iterator(); + Section i2; + + @Override /* Iterator */ + public boolean hasNext() { + return i.hasNext(); + } + + @Override /* Iterator */ + public Section next() { + i2 = i.next(); + return i2; + } + + @Override /* Iterator */ + public void remove() { + Set<String> changes = createChanges(); + findChanges(changes, i2, null); + i.remove(); + signalChanges(changes); + } + }; + } + @Override /* Collection */ + public int size() { + return sections.size(); + } + }; + } + + @Override /* Map */ + public Section remove(Object key) { + Set<String> changes = createChanges(); + Section prev = remove(key, changes); + signalChanges(changes); + return prev; + } + + private Section remove(Object key, Set<String> changes) { + writeLock(); + try { + Section prev = sections.remove(key); + findChanges(changes, prev, null); + return prev; + } finally { + writeUnlock(); + } + } + + //-------------------------------------------------------------------------------- + // API methods + //-------------------------------------------------------------------------------- + + @Override /* ConfigFile */ + public String get(String sectionName, String sectionKey) { + assertFieldNotNull(sectionKey, "sectionKey"); + Section s = get(sectionName); + if (s == null) + return null; + Object s2 = s.get(sectionKey); + return (s2 == null ? null : s2.toString()); + } + + @Override /* ConfigFile */ + public String put(String sectionName, String sectionKey, Object value, boolean encoded) { + assertFieldNotNull(sectionKey, "sectionKey"); + Section s = getSection(sectionName, true); + return s.put(sectionKey, value.toString(), encoded); + } + + @Override /* ConfigFile */ + public String remove(String sectionName, String sectionKey) { + assertFieldNotNull(sectionKey, "sectionKey"); + Section s = getSection(sectionName, false); + if (s == null) + return null; + return s.remove(sectionKey); + } + + @Override /* ConfigFile */ + public ConfigFileImpl addLines(String section, String...lines) { + Set<String> changes = createChanges(); + writeLock(); + try { + getSection(section, true).addLines(changes, lines); + } finally { + writeUnlock(); + } + signalChanges(changes); + return this; + } + + @Override /* ConfigFile */ + public ConfigFileImpl addHeaderComments(String section, String...headerComments) { + writeLock(); + try { + if (headerComments == null) + headerComments = new String[0]; + getSection(section, true).addHeaderComments(Arrays.asList(headerComments)); + } finally { + writeUnlock(); + } + return this; + } + + @Override /* ConfigFile */ + public ConfigFileImpl clearHeaderComments(String section) { + writeLock(); + try { + Section s = getSection(section, false); + if (s != null) + s.clearHeaderComments(); + } finally { + writeUnlock(); + } + return this; + } + + @Override /* ConfigFile */ + public Section getSection(String name) { + return getSection(name, false); + } + + @Override /* ConfigFile */ + public Section getSection(String name, boolean create) { + if (StringUtils.isEmpty(name)) + name = DEFAULT; + Section s = sections.get(name); + if (s != null) + return s; + if (create) { + s = new Section().setParent(this).setName(name); + sections.put(name, s); + return s; + } + return null; + } + + @Override /* ConfigFile */ + public ConfigFileImpl addSection(String name) { + writeLock(); + try { + getSection(name, true); + } finally { + writeUnlock(); + } + return this; + } + + @Override /* ConfigFile */ + public ConfigFile setSection(String name, Map<String,String> contents) { + writeLock(); + try { + put(name, new Section(contents).setParent(this).setName(name)); + } finally { + writeUnlock(); + } + return this; + } + + @Override /* ConfigFile */ + public ConfigFileImpl removeSection(String name) { + Set<String> changes = createChanges(); + writeLock(); + try { + Section prev = sections.remove(name); + if (changes != null && prev != null) + findChanges(changes, prev, null); + } finally { + writeUnlock(); + } + signalChanges(changes); + return this; + } + + @Override /* ConfigFile */ + public Set<String> getSectionKeys(String sectionName) { + Section s = get(sectionName); + if (s == null) + return null; + return s.keySet(); + } + + @Override /* ConfigFile */ + public boolean isEncoded(String key) { + assertFieldNotNull(key, "key"); + String section = getSectionName(key); + Section s = getSection(section, false); + if (s == null) + return false; + return s.isEncoded(getSectionKey(key)); + } + + @Override /* ConfigFile */ + public ConfigFileImpl save() throws IOException { + writeLock(); + try { + if (file == null) + throw new UnsupportedOperationException("No backing file specified for config file."); + Writer out = new OutputStreamWriter(new FileOutputStream(file), charset); + try { + serializeTo(out); + hasBeenModified = false; + modifiedTimestamp = file.lastModified(); + } finally { + out.close(); + } + for (ConfigFileListener l : listeners) + l.onSave(this); + return this; + } finally { + writeUnlock(); + } + } + + @Override /* ConfigFile */ + public ConfigFileImpl serializeTo(Writer out, ConfigFileFormat format) throws IOException { + readLock(); + try { + PrintWriter pw = (out instanceof PrintWriter ? (PrintWriter)out : new PrintWriter(out)); + for (Section s : sections.values()) + s.writeTo(pw, format); + pw.flush(); + pw.close(); + out.close(); + } finally { + readUnlock(); + } + return this; + } + + void setHasBeenModified() { + hasBeenModified = true; + } + + @Override /* ConfigFile */ + public String toString() { + try { + StringWriter sw = new StringWriter(); + toWritable().writeTo(sw); + return sw.toString(); + } catch (IOException e) { + return e.getLocalizedMessage(); + } + } + + @Override /* ConfigFile */ + public ConfigFile addListener(ConfigFileListener listener) { + assertFieldNotNull(listener, "listener"); + writeLock(); + try { + this.listeners.add(listener); + return this; + } finally { + writeUnlock(); + } + } + + List<ConfigFileListener> getListeners() { + return listeners; + } + + @Override /* ConfigFile */ + public Writable toWritable() { + return new ConfigFileWritable(this); + } + + @Override /* ConfigFile */ + public ConfigFile merge(ConfigFile cf) { + assertFieldNotNull(cf, "cf"); + Set<String> changes = createChanges(); + writeLock(); + try { + for (String sectionName : this.keySet()) + if (! cf.containsKey(sectionName)) + remove(sectionName, changes); + + for (Map.Entry<String,Section> e : cf.entrySet()) + put(e.getKey(), e.getValue(), changes); + + } finally { + writeUnlock(); + } + signalChanges(changes); + return this; + } + + Encoder getEncoder() { + return encoder; + } + + @Override /* ConfigFile */ + protected WriterSerializer getSerializer() throws SerializeException { + if (serializer == null) + throw new SerializeException("Serializer not defined on config file."); + return serializer; + } + + @Override /* ConfigFile */ + protected ReaderParser getParser() throws ParseException { + if (parser == null) + throw new ParseException("Parser not defined on config file."); + return parser; + } + + @Override /* ConfigFile */ + protected void readLock() { + lock.readLock().lock(); + } + + @Override /* ConfigFile */ + protected void readUnlock() { + lock.readLock().unlock(); + } + + private void writeLock() { + if (readOnly) + throw new UnsupportedOperationException("Cannot modify read-only ConfigFile."); + lock.writeLock().lock(); + hasBeenModified = true; + } + + private void writeUnlock() { + lock.writeLock().unlock(); + } + + @Override /* ConfigFile */ + public ConfigFile getResolving(StringVarResolver vr) { + assertFieldNotNull(vr, "vr"); + return new ConfigFileWrapped(this, vr); + } + + @Override /* ConfigFile */ + public ConfigFile getResolving() { + return getResolving( + new StringVarResolver(StringVarResolver.DEFAULT) + .addVar("C", new StringVarWithDefault() { + @Override /* StringVar */ + public String resolve(String varVal) { + return getString(varVal); + } + } + ) + ); + } + + /* + * Finds the keys that are different between the two sections and adds it to + * the specified set. + */ + private void findChanges(Set<String> s, Section a, Section b) { + if (s == null) + return; + String sname = (a == null ? b.name : a.name); + if (a == null) { + for (String k : b.keySet()) + s.add(getFullKey(sname, k)); + } else if (b == null) { + for (String k : a.keySet()) + s.add(getFullKey(sname, k)); + } else { + for (String k : a.keySet()) + addChange(s, sname, k, a.get(k), b.get(k)); + for (String k : b.keySet()) + addChange(s, sname, k, a.get(k), b.get(k)); + } + } + + private void addChange(Set<String> changes, String section, String key, String oldVal, String newVal) { + if (! StringUtils.isEquals(oldVal, newVal)) + changes.add(getFullKey(section, key)); + } + + private Set<String> createChanges() { + return (listeners.size() > 0 ? new LinkedHashSet<String>() : null); + } + + private void signalChanges(Set<String> changes) { + if (changes != null && ! changes.isEmpty()) + for (ConfigFileListener l : listeners) + l.onChange(this, changes); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileListener.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileListener.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileListener.class new file mode 100755 index 0000000..2e64bfd Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileListener.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileListener.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileListener.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileListener.java new file mode 100755 index 0000000..a813356 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileListener.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.ini; + +import java.util.*; + + +/** + * Listener that can be used to listen for change events in config files. + * <p> + * Use the {@link ConfigFile#addListener(ConfigFileListener)} method to register listeners. + */ +public class ConfigFileListener { + + /** + * Gets called immediately after a config file has been loaded. + * + * @param cf The config file being loaded. + */ + public void onLoad(ConfigFile cf) {} + + /** + * Gets called immediately after a config file has been saved. + * + * @param cf The config file being saved. + */ + public void onSave(ConfigFile cf) {} + + /** + * Signifies that the specified values have changed. + * + * @param cf The config file being modified. + * @param changes The full keys (e.g. <js>"Section/key"</js>) of entries that have changed in the config file. + */ + public void onChange(ConfigFile cf, Set<String> changes) {} +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWrapped.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWrapped.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWrapped.class new file mode 100755 index 0000000..bd1a0af Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWrapped.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWrapped.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWrapped.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWrapped.java new file mode 100755 index 0000000..a0bbfd1 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWrapped.java @@ -0,0 +1,259 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.ini; +import static com.ibm.juno.core.utils.ThrowableUtils.*; + +import java.io.*; +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.parser.*; +import com.ibm.juno.core.serializer.*; +import com.ibm.juno.core.utils.*; + +/** + * Wraps an instance of {@link ConfigFileImpl} in an interface that will + * automatically replace {@link StringVarResolver} variables. + * <p> + * The {@link ConfigFile#getResolving(StringVarResolver)} returns an instance of this class. + * <p> + * This class overrides the {@link #getString(String, String)} to resolve string variables. + * All other method calls are passed through to the inner config file. + * + * @author James Bognar ([email protected]) + */ +public final class ConfigFileWrapped extends ConfigFile { + + private final ConfigFileImpl cf; + private final StringVarResolver vr; + + ConfigFileWrapped(ConfigFileImpl cf, StringVarResolver vr) { + this.cf = cf; + this.vr = vr; + } + + @Override /* ConfigFile */ + public void clear() { + cf.clear(); + } + + @Override /* ConfigFile */ + public boolean containsKey(Object key) { + return cf.containsKey(key); + } + + @Override /* ConfigFile */ + public boolean containsValue(Object value) { + return cf.containsValue(value); + } + + @Override /* ConfigFile */ + public Set<java.util.Map.Entry<String,Section>> entrySet() { + return cf.entrySet(); + } + + @Override /* ConfigFile */ + public Section get(Object key) { + return cf.get(key); + } + + @Override /* ConfigFile */ + public boolean isEmpty() { + return cf.isEmpty(); + } + + @Override /* ConfigFile */ + public Set<String> keySet() { + return cf.keySet(); + } + + @Override /* ConfigFile */ + public Section put(String key, Section value) { + return cf.put(key, value); + } + + @Override /* ConfigFile */ + public void putAll(Map<? extends String,? extends Section> map) { + cf.putAll(map); + } + + @Override /* ConfigFile */ + public Section remove(Object key) { + return cf.remove(key); + } + + @Override /* ConfigFile */ + public int size() { + return cf.size(); + } + + @Override /* ConfigFile */ + public Collection<Section> values() { + return cf.values(); + } + + @Override /* ConfigFile */ + public ConfigFile loadIfModified() throws IOException { + cf.loadIfModified(); + return this; + } + + @Override /* ConfigFile */ + public ConfigFile load() throws IOException { + cf.load(); + return this; + } + + @Override /* ConfigFile */ + public ConfigFile load(Reader r) throws IOException { + cf.load(r); + return this; + } + + + @Override /* ConfigFile */ + public boolean isEncoded(String key) { + return cf.isEncoded(key); + } + + @Override /* ConfigFile */ + public ConfigFile addLines(String section, String... lines) { + cf.addLines(section, lines); + return this; + } + + @Override /* ConfigFile */ + public ConfigFile addHeaderComments(String section, String... headerComments) { + cf.addHeaderComments(section, headerComments); + return this; + } + + @Override /* ConfigFile */ + public ConfigFile clearHeaderComments(String section) { + cf.clearHeaderComments(section); + return this; + } + + @Override /* ConfigFile */ + public Section getSection(String name) { + return cf.getSection(name); + } + + @Override /* ConfigFile */ + public Section getSection(String name, boolean create) { + return cf.getSection(name, create); + } + + @Override /* ConfigFile */ + public ConfigFile addSection(String name) { + cf.addSection(name); + return this; + } + + @Override /* ConfigFile */ + public ConfigFile setSection(String name, Map<String,String> contents) { + cf.setSection(name, contents); + return this; + } + + @Override /* ConfigFile */ + public ConfigFile removeSection(String name) { + cf.removeSection(name); + return this; + } + + @Override /* ConfigFile */ + public ConfigFile save() throws IOException { + cf.save(); + return this; + } + + @Override /* ConfigFile */ + public ConfigFile serializeTo(Writer out, ConfigFileFormat format) throws IOException { + cf.serializeTo(out, format); + return this; + } + + @Override /* ConfigFile */ + public String toString() { + return cf.toString(); + } + + @Override /* ConfigFile */ + @SuppressWarnings("hiding") + public ConfigFile getResolving(StringVarResolver vr) { + assertFieldNotNull(vr, "vr"); + return new ConfigFileWrapped(cf, vr); + } + + @Override /* ConfigFile */ + public ConfigFile getResolving() { + return new ConfigFileWrapped(cf, StringVarResolver.DEFAULT); + } + + @Override /* ConfigFile */ + public ConfigFile addListener(ConfigFileListener listener) { + cf.addListener(listener); + return this; + } + + @Override /* ConfigFile */ + public Writable toWritable() { + return cf.toWritable(); + } + + @Override /* ConfigFile */ + public ConfigFile merge(ConfigFile newCf) { + cf.merge(newCf); + return this; + } + + @Override /* ConfigFile */ + protected WriterSerializer getSerializer() throws SerializeException { + return cf.getSerializer(); + } + + @Override /* ConfigFile */ + protected ReaderParser getParser() throws ParseException { + return cf.getParser(); + } + + @Override /* ConfigFile */ + public String get(String sectionName, String sectionKey) { + String s = cf.get(sectionName, sectionKey); + if (s == null) + return null; + return vr.resolve(s); + } + + @Override /* ConfigFile */ + public String put(String sectionName, String sectionKey, Object value, boolean encoded) { + return cf.put(sectionName, sectionKey, value, encoded); + } + + @Override /* ConfigFile */ + public String remove(String sectionName, String sectionKey) { + return cf.remove(sectionName, sectionKey); + } + + @Override /* ConfigFile */ + public Set<String> getSectionKeys(String sectionName) { + return cf.getSectionKeys(sectionName); + } + + @Override /* ConfigFile */ + protected void readLock() { + cf.readLock(); + } + + @Override /* ConfigFile */ + protected void readUnlock() { + cf.readUnlock(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWritable.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWritable.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWritable.class new file mode 100755 index 0000000..ff6dfa8 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ini/ConfigFileWritable.class differ
