diff --git a/build.sbt b/build.sbt index d8e8d1da86..2ec4bdaa23 100644 --- a/build.sbt +++ b/build.sbt @@ -219,9 +219,7 @@ lazy val scripts = project("scripts") "software.amazon.awssdk" % "s3" % awsSdkV2Version, "software.amazon.awssdk" % "dynamodb" % awsSdkV2Version, // bump jcommander explicitly as AWS SDK is pulling in a vulnerable version - "com.beust" % "jcommander" % "1.75", "org.apache.commons" % "commons-compress" % "1.27.1", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.6.4" ) ) diff --git a/scripts/src/main/java/com/gu/typesafe/LICENSE-2.0.txt b/scripts/src/main/java/com/gu/typesafe/LICENSE-2.0.txt deleted file mode 100644 index b6129c0abd..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/LICENSE-2.0.txt +++ /dev/null @@ -1,203 +0,0 @@ -License below for the shaded copy of Typesafe Config from https://github.com/lightbend/config: - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/scripts/src/main/java/com/gu/typesafe/config/Config.java b/scripts/src/main/java/com/gu/typesafe/config/Config.java deleted file mode 100644 index 70e808f92e..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/Config.java +++ /dev/null @@ -1,1139 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -import java.time.Duration; -import java.time.Period; -import java.time.temporal.TemporalAmount; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -/** - * An immutable map from config paths to config values. Paths are dot-separated - * expressions such as foo.bar.baz. Values are as in JSON - * (booleans, strings, numbers, lists, or objects), represented by - * {@link com.gu.typesafe.config.ConfigValue} instances. Values accessed through the - * Config interface are never null. - * - *

- * {@code Config} is an immutable object and thus safe to use from multiple - * threads. There's never a need for "defensive copies." - * - *

- * Fundamental operations on a {@code Config} include getting configuration - * values, resolving substitutions with {@link Config#resolve()}, and - * merging configs using {@link Config#withFallback(com.gu.typesafe.config.ConfigMergeable)}. - * - *

- * All operations return a new immutable {@code Config} rather than modifying - * the original instance. - * - *

- * Examples - * - *

- * You can find an example app and library on - * GitHub. Also be sure to read the package overview which - * describes the big picture as shown in those examples. - * - *

- * Paths, keys, and Config vs. ConfigObject - * - *

- * Config is a view onto a tree of {@link com.gu.typesafe.config.ConfigObject}; the - * corresponding object tree can be found through {@link Config#root()}. - * ConfigObject is a map from config keys, rather than - * paths, to config values. Think of ConfigObject as a JSON object - * and Config as a configuration API. - * - *

- * The API tries to consistently use the terms "key" and "path." A key is a key - * in a JSON object; it's just a string that's the key in a map. A "path" is a - * parseable expression with a syntax and it refers to a series of keys. Path - * expressions are described in the spec for - * Human-Optimized Config Object Notation. In brief, a path is - * period-separated so "a.b.c" looks for key c in object b in object a in the - * root object. Sometimes double quotes are needed around special characters in - * path expressions. - * - *

- * The API for a {@code Config} is in terms of path expressions, while the API - * for a {@code ConfigObject} is in terms of keys. Conceptually, {@code Config} - * is a one-level map from paths to values, while a - * {@code ConfigObject} is a tree of nested maps from keys to values. - * - *

- * Use {@link com.gu.typesafe.config.ConfigUtil#joinPath} and {@link com.gu.typesafe.config.ConfigUtil#splitPath} to convert - * between path expressions and individual path elements (keys). - * - *

- * Another difference between {@code Config} and {@code ConfigObject} is that - * conceptually, {@code ConfigValue}s with a {@link com.gu.typesafe.config.ConfigValue#valueType() - * valueType()} of {@link ConfigValueType#NULL NULL} exist in a - * {@code ConfigObject}, while a {@code Config} treats null values as if they - * were missing. (With the exception of two methods: {@link Config#hasPathOrNull} - * and {@link Config#getIsNull} let you detect null values.) - * - *

- * Getting configuration values - * - *

- * The "getters" on a {@code Config} all work in the same way. They never return - * null, nor do they return a {@code ConfigValue} with - * {@link com.gu.typesafe.config.ConfigValue#valueType() valueType()} of {@link ConfigValueType#NULL - * NULL}. Instead, they throw {@link com.gu.typesafe.config.ConfigException.Missing} if the value is - * completely absent or set to null. If the value is set to null, a subtype of - * {@code ConfigException.Missing} called {@link com.gu.typesafe.config.ConfigException.Null} will be - * thrown. {@link com.gu.typesafe.config.ConfigException.WrongType} will be thrown anytime you ask for - * a type and the value has an incompatible type. Reasonable type conversions - * are performed for you though. - * - *

- * Iteration - * - *

- * If you want to iterate over the contents of a {@code Config}, you can get its - * {@code ConfigObject} with {@link #root()}, and then iterate over the - * {@code ConfigObject} (which implements java.util.Map). Or, you - * can use {@link #entrySet()} which recurses the object tree for you and builds - * up a Set of all path-value pairs where the value is not null. - * - *

- * Resolving substitutions - * - *

- * Substitutions are the ${foo.bar} syntax in config - * files, described in the specification. Resolving substitutions replaces these references with real - * values. - * - *

- * Before using a {@code Config} it's necessary to call {@link Config#resolve()} - * to handle substitutions (though {@link com.gu.typesafe.config.ConfigFactory#load()} and similar - * methods will do the resolve for you already). - * - *

- * Merging - * - *

- * The full Config for your application can be constructed using - * the associative operation {@link Config#withFallback(com.gu.typesafe.config.ConfigMergeable)}. If - * you use {@link com.gu.typesafe.config.ConfigFactory#load()} (recommended), it merges system - * properties over the top of application.conf over the top of - * reference.conf, using withFallback. You can add in - * additional sources of configuration in the same way (usually, custom layers - * should go either just above or just below application.conf, - * keeping reference.conf at the bottom and system properties at - * the top). - * - *

- * Serialization - * - *

- * Convert a Config to a JSON or HOCON string by calling - * {@link com.gu.typesafe.config.ConfigObject#render()} on the root object, - * myConfig.root().render(). There's also a variant - * {@link com.gu.typesafe.config.ConfigObject#render(com.gu.typesafe.config.ConfigRenderOptions)} which allows you to control - * the format of the rendered string. (See {@link ConfigRenderOptions}.) Note - * that Config does not remember the formatting of the original - * file, so if you load, modify, and re-save a config file, it will be - * substantially reformatted. - * - *

- * As an alternative to {@link com.gu.typesafe.config.ConfigObject#render()}, the - * toString() method produces a debug-output-oriented - * representation (which is not valid JSON). - * - *

- * Java serialization is supported as well for Config and all - * subtypes of ConfigValue. - * - *

- * This is an interface but don't implement it yourself - * - *

- * Do not implement {@code Config}; it should only be implemented by - * the config library. Arbitrary implementations will not work because the - * library internals assume a specific concrete implementation. Also, this - * interface is likely to grow new methods over time, so third-party - * implementations will break. - */ -public interface Config extends com.gu.typesafe.config.ConfigMergeable { - /** - * Gets the {@code Config} as a tree of {@link com.gu.typesafe.config.ConfigObject}. This is a - * constant-time operation (it is not proportional to the number of values - * in the {@code Config}). - * - * @return the root object in the configuration - */ - com.gu.typesafe.config.ConfigObject root(); - - /** - * Gets the origin of the {@code Config}, which may be a file, or a file - * with a line number, or just a descriptive phrase. - * - * @return the origin of the {@code Config} for use in error messages - */ - ConfigOrigin origin(); - - @Override - Config withFallback(ConfigMergeable other); - - /** - * Returns a replacement config with all substitutions (the - * ${foo.bar} syntax, see the - * spec) resolved. Substitutions are looked up using this - * Config as the root object, that is, a substitution - * ${foo.bar} will be replaced with the result of - * getValue("foo.bar"). - * - *

- * This method uses {@link com.gu.typesafe.config.ConfigResolveOptions#defaults()}, there is - * another variant {@link Config#resolve(com.gu.typesafe.config.ConfigResolveOptions)} which lets - * you specify non-default options. - * - *

- * A given {@link Config} must be resolved before using it to retrieve - * config values, but ideally should be resolved one time for your entire - * stack of fallbacks (see {@link Config#withFallback}). Otherwise, some - * substitutions that could have resolved with all fallbacks available may - * not resolve, which will be potentially confusing for your application's - * users. - * - *

- * resolve() should be invoked on root config objects, rather - * than on a subtree (a subtree is the result of something like - * config.getConfig("foo")). The problem with - * resolve() on a subtree is that substitutions are relative to - * the root of the config and the subtree will have no way to get values - * from the root. For example, if you did - * config.getConfig("foo").resolve() on the below config file, - * it would not work: - * - *

-     *   common-value = 10
-     *   foo {
-     *      whatever = ${common-value}
-     *   }
-     * 
- * - *

- * Many methods on {@link com.gu.typesafe.config.ConfigFactory} such as - * {@link ConfigFactory#load()} automatically resolve the loaded - * Config on the loaded stack of config files. - * - *

- * Resolving an already-resolved config is a harmless no-op, but again, it - * is best to resolve an entire stack of fallbacks (such as all your config - * files combined) rather than resolving each one individually. - * - * @return an immutable object with substitutions resolved - * @throws com.gu.typesafe.config.ConfigException.UnresolvedSubstitution - * if any substitutions refer to nonexistent paths - * @throws com.gu.typesafe.config.ConfigException - * some other config exception if there are other problems - */ - Config resolve(); - - /** - * Like {@link Config#resolve()} but allows you to specify non-default - * options. - * - * @param options - * resolve options - * @return the resolved Config (may be only partially resolved if options are set to allow unresolved) - */ - Config resolve(com.gu.typesafe.config.ConfigResolveOptions options); - - /** - * Checks whether the config is completely resolved. After a successful call - * to {@link Config#resolve()} it will be completely resolved, but after - * calling {@link Config#resolve(com.gu.typesafe.config.ConfigResolveOptions)} with - * allowUnresolved set in the options, it may or may not be - * completely resolved. A newly-loaded config may or may not be completely - * resolved depending on whether there were substitutions present in the - * file. - * - * @return true if there are no unresolved substitutions remaining in this - * configuration. - * @since 1.2.0 - */ - boolean isResolved(); - - /** - * Like {@link Config#resolve()} except that substitution values are looked - * up in the given source, rather than in this instance. This is a - * special-purpose method which doesn't make sense to use in most cases; - * it's only needed if you're constructing some sort of app-specific custom - * approach to configuration. The more usual approach if you have a source - * of substitution values would be to merge that source into your config - * stack using {@link Config#withFallback} and then resolve. - *

- * Note that this method does NOT look in this instance for substitution - * values. If you want to do that, you could either merge this instance into - * your value source using {@link Config#withFallback}, or you could resolve - * multiple times with multiple sources (using - * {@link com.gu.typesafe.config.ConfigResolveOptions#setAllowUnresolved(boolean)} so the partial - * resolves don't fail). - * - * @param source - * configuration to pull values from - * @return an immutable object with substitutions resolved - * @throws com.gu.typesafe.config.ConfigException.UnresolvedSubstitution - * if any substitutions refer to paths which are not in the - * source - * @throws com.gu.typesafe.config.ConfigException - * some other config exception if there are other problems - * @since 1.2.0 - */ - Config resolveWith(Config source); - - /** - * Like {@link Config#resolveWith(Config)} but allows you to specify - * non-default options. - * - * @param source - * source configuration to pull values from - * @param options - * resolve options - * @return the resolved Config (may be only partially resolved - * if options are set to allow unresolved) - * @since 1.2.0 - */ - Config resolveWith(Config source, ConfigResolveOptions options); - - /** - * Validates this config against a reference config, throwing an exception - * if it is invalid. The purpose of this method is to "fail early" with a - * comprehensive list of problems; in general, anything this method can find - * would be detected later when trying to use the config, but it's often - * more user-friendly to fail right away when loading the config. - * - *

- * Using this method is always optional, since you can "fail late" instead. - * - *

- * You must restrict validation to paths you "own" (those whose meaning are - * defined by your code module). If you validate globally, you may trigger - * errors about paths that happen to be in the config but have nothing to do - * with your module. It's best to allow the modules owning those paths to - * validate them. Also, if every module validates only its own stuff, there - * isn't as much redundant work being done. - * - *

- * If no paths are specified in checkValid()'s parameter list, - * validation is for the entire config. - * - *

- * If you specify paths that are not in the reference config, those paths - * are ignored. (There's nothing to validate.) - * - *

- * Here's what validation involves: - * - *

- * - *

- * If you want to allow a certain setting to have a flexible type (or - * otherwise want validation to be looser for some settings), you could - * either remove the problematic setting from the reference config provided - * to this method, or you could intercept the validation exception and - * screen out certain problems. Of course, this will only work if all other - * callers of this method are careful to restrict validation to their own - * paths, as they should be. - * - *

- * If validation fails, the thrown exception contains a list of all problems - * found. See {@link com.gu.typesafe.config.ConfigException.ValidationFailed#problems}. The - * exception's getMessage() will have all the problems - * concatenated into one huge string, as well. - * - *

- * Again, checkValid() can't guess every domain-specific way a - * setting can be invalid, so some problems may arise later when attempting - * to use the config. checkValid() is limited to reporting - * generic, but common, problems such as missing settings and blatant type - * incompatibilities. - * - * @param reference - * a reference configuration - * @param restrictToPaths - * only validate values underneath these paths that your code - * module owns and understands - * @throws com.gu.typesafe.config.ConfigException.ValidationFailed - * if there are any validation issues - * @throws com.gu.typesafe.config.ConfigException.NotResolved - * if this config is not resolved - * @throws com.gu.typesafe.config.ConfigException.BugOrBroken - * if the reference config is unresolved or caller otherwise - * misuses the API - */ - void checkValid(Config reference, String... restrictToPaths); - - /** - * Checks whether a value is present and non-null at the given path. This - * differs in two ways from {@code Map.containsKey()} as implemented by - * {@link com.gu.typesafe.config.ConfigObject}: it looks for a path expression, not a key; and it - * returns false for null values, while {@code containsKey()} returns true - * indicating that the object contains a null value for the key. - * - *

- * If a path exists according to {@link #hasPath(String)}, then - * {@link #getValue(String)} will never throw an exception. However, the - * typed getters, such as {@link #getInt(String)}, will still throw if the - * value is not convertible to the requested type. - * - *

- * Note that path expressions have a syntax and sometimes require quoting - * (see {@link com.gu.typesafe.config.ConfigUtil#joinPath} and {@link com.gu.typesafe.config.ConfigUtil#splitPath}). - * - * @param path - * the path expression - * @return true if a non-null value is present at the path - * @throws com.gu.typesafe.config.ConfigException.BadPath - * if the path expression is invalid - */ - boolean hasPath(String path); - - /** - * Checks whether a value is present at the given path, even - * if the value is null. Most of the getters on - * Config will throw if you try to get a null - * value, so if you plan to call {@link #getValue(String)}, - * {@link #getInt(String)}, or another getter you may want to - * use plain {@link #hasPath(String)} rather than this method. - * - *

- * To handle all three cases (unset, null, and a non-null value) - * the code might look like: - *


-     * if (config.hasPathOrNull(path)) {
-     *     if (config.getIsNull(path)) {
-     *        // handle null setting
-     *     } else {
-     *        // get and use non-null setting
-     *     }
-     * } else {
-     *     // handle entirely unset path
-     * }
-     * 
- * - *

However, the usual thing is to allow entirely unset - * paths to be a bug that throws an exception (because you set - * a default in your reference.conf), so in that - * case it's OK to call {@link #getIsNull(String)} without - * checking hasPathOrNull first. - * - *

- * Note that path expressions have a syntax and sometimes require quoting - * (see {@link com.gu.typesafe.config.ConfigUtil#joinPath} and {@link com.gu.typesafe.config.ConfigUtil#splitPath}). - * - * @param path - * the path expression - * @return true if a value is present at the path, even if the value is null - * @throws com.gu.typesafe.config.ConfigException.BadPath - * if the path expression is invalid - */ - boolean hasPathOrNull(String path); - - /** - * Returns true if the {@code Config}'s root object contains no key-value - * pairs. - * - * @return true if the configuration is empty - */ - boolean isEmpty(); - - /** - * Returns the set of path-value pairs, excluding any null values, found by - * recursing {@link #root() the root object}. Note that this is very - * different from root().entrySet() which returns the set of - * immediate-child keys in the root object and includes null values. - *

- * Entries contain path expressions meaning there may be quoting - * and escaping involved. Parse path expressions with - * {@link com.gu.typesafe.config.ConfigUtil#splitPath}. - *

- * Because a Config is conceptually a single-level map from - * paths to values, there will not be any {@link com.gu.typesafe.config.ConfigObject} values in the - * entries (that is, all entries represent leaf nodes). Use - * {@link com.gu.typesafe.config.ConfigObject} rather than Config if you want a tree. - * (OK, this is a slight lie: Config entries may contain - * {@link com.gu.typesafe.config.ConfigList} and the lists may contain objects. But no objects are - * directly included as entry values.) - * - * @return set of paths with non-null values, built up by recursing the - * entire tree of {@link com.gu.typesafe.config.ConfigObject} and creating an entry for - * each leaf value. - */ - Set> entrySet(); - - /** - * Checks whether a value is set to null at the given path, - * but throws an exception if the value is entirely - * unset. This method will not throw if {@link - * #hasPathOrNull(String)} returned true for the same path, so - * to avoid any possible exception check - * hasPathOrNull() first. However, an exception - * for unset paths will usually be the right thing (because a - * reference.conf should exist that has the path - * set, the path should never be unset unless something is - * broken). - * - *

- * Note that path expressions have a syntax and sometimes require quoting - * (see {@link com.gu.typesafe.config.ConfigUtil#joinPath} and {@link com.gu.typesafe.config.ConfigUtil#splitPath}). - * - * @param path - * the path expression - * @return true if the value exists and is null, false if it - * exists and is not null - * @throws com.gu.typesafe.config.ConfigException.BadPath - * if the path expression is invalid - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is not set at all - */ - boolean getIsNull(String path); - - /** - * - * @param path - * path expression - * @return the boolean value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to boolean - */ - boolean getBoolean(String path); - - /** - * @param path - * path expression - * @return the numeric value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a number - */ - Number getNumber(String path); - - /** - * Gets the integer at the given path. If the value at the - * path has a fractional (floating point) component, it - * will be discarded and only the integer part will be - * returned (it works like a "narrowing primitive conversion" - * in the Java language specification). - * - * @param path - * path expression - * @return the 32-bit integer value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to an int (for example it is out - * of range, or it's a boolean value) - */ - int getInt(String path); - - /** - * Gets the long integer at the given path. If the value at - * the path has a fractional (floating point) component, it - * will be discarded and only the integer part will be - * returned (it works like a "narrowing primitive conversion" - * in the Java language specification). - * - * @param path - * path expression - * @return the 64-bit long value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a long - */ - long getLong(String path); - - /** - * @param path - * path expression - * @return the floating-point value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a double - */ - double getDouble(String path); - - /** - * @param path - * path expression - * @return the string value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a string - */ - String getString(String path); - - /** - * @param enumClass - * an enum class - * @param - * a generic denoting a specific type of enum - * @param path - * path expression - * @return the {@code Enum} value at the requested path - * of the requested enum class - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to an Enum - */ - public > T getEnum(Class enumClass, String path); - - /** - * @param path - * path expression - * @return the {@link com.gu.typesafe.config.ConfigObject} value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to an object - */ - com.gu.typesafe.config.ConfigObject getObject(String path); - - /** - * @param path - * path expression - * @return the nested {@code Config} value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a Config - */ - Config getConfig(String path); - - /** - * Gets the value at the path as an unwrapped Java boxed value ( - * {@link java.lang.Boolean Boolean}, {@link java.lang.Integer Integer}, and - * so on - see {@link com.gu.typesafe.config.ConfigValue#unwrapped()}). - * - * @param path - * path expression - * @return the unwrapped value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - */ - Object getAnyRef(String path); - - /** - * Gets the value at the given path, unless the value is a - * null value or missing, in which case it throws just like - * the other getters. Use {@code get()} on the {@link - * Config#root()} object (or other object in the tree) if you - * want an unprocessed value. - * - * @param path - * path expression - * @return the value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - */ - com.gu.typesafe.config.ConfigValue getValue(String path); - - /** - * Gets a value as a size in bytes (parses special strings like "128M"). If - * the value is already a number, then it's left alone; if it's a string, - * it's parsed understanding unit suffixes such as "128K", as documented in - * the the - * spec. - * - * @param path - * path expression - * @return the value at the requested path, in bytes - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to Long or String - * @throws com.gu.typesafe.config.ConfigException.BadValue - * if value cannot be parsed as a size in bytes - */ - Long getBytes(String path); - - /** - * Gets a value as an amount of memory (parses special strings like "128M"). If - * the value is already a number, then it's left alone; if it's a string, - * it's parsed understanding unit suffixes such as "128K", as documented in - * the the - * spec. - * - * @since 1.3.0 - * - * @param path - * path expression - * @return the value at the requested path, in bytes - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to Long or String - * @throws com.gu.typesafe.config.ConfigException.BadValue - * if value cannot be parsed as a size in bytes - */ - com.gu.typesafe.config.ConfigMemorySize getMemorySize(String path); - - /** - * Get value as a duration in milliseconds. If the value is already a - * number, then it's left alone; if it's a string, it's parsed understanding - * units suffixes like "10m" or "5ns" as documented in the the - * spec. - * - * @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)} - * - * @param path - * path expression - * @return the duration value at the requested path, in milliseconds - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to Long or String - * @throws com.gu.typesafe.config.ConfigException.BadValue - * if value cannot be parsed as a number of milliseconds - */ - @Deprecated Long getMilliseconds(String path); - - /** - * Get value as a duration in nanoseconds. If the value is already a number - * it's taken as milliseconds and converted to nanoseconds. If it's a - * string, it's parsed understanding unit suffixes, as for - * {@link #getDuration(String, TimeUnit)}. - * - * @deprecated As of release 1.1, replaced by {@link #getDuration(String, TimeUnit)} - * - * @param path - * path expression - * @return the duration value at the requested path, in nanoseconds - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to Long or String - * @throws com.gu.typesafe.config.ConfigException.BadValue - * if value cannot be parsed as a number of nanoseconds - */ - @Deprecated Long getNanoseconds(String path); - - /** - * Gets a value as a duration in a specified - * {@link java.util.concurrent.TimeUnit TimeUnit}. If the value is already a - * number, then it's taken as milliseconds and then converted to the - * requested TimeUnit; if it's a string, it's parsed understanding units - * suffixes like "10m" or "5ns" as documented in the the - * spec. - * - * @since 1.2.0 - * - * @param path - * path expression - * @param unit - * convert the return value to this time unit - * @return the duration value at the requested path, in the given TimeUnit - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to Long or String - * @throws com.gu.typesafe.config.ConfigException.BadValue - * if value cannot be parsed as a number of the given TimeUnit - */ - long getDuration(String path, TimeUnit unit); - - /** - * Gets a value as a java.time.Duration. If the value is - * already a number, then it's taken as milliseconds; if it's - * a string, it's parsed understanding units suffixes like - * "10m" or "5ns" as documented in the the - * spec. This method never returns null. - * - * @since 1.3.0 - * - * @param path - * path expression - * @return the duration value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to Long or String - * @throws com.gu.typesafe.config.ConfigException.BadValue - * if value cannot be parsed as a number of the given TimeUnit - */ - Duration getDuration(String path); - - /** - * Gets a value as a java.time.Period. If the value is - * already a number, then it's taken as days; if it's - * a string, it's parsed understanding units suffixes like - * "10d" or "5w" as documented in the the - * spec. This method never returns null. - * - * @since 1.3.0 - * - * @param path - * path expression - * @return the period value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to Long or String - * @throws com.gu.typesafe.config.ConfigException.BadValue - * if value cannot be parsed as a number of the given TimeUnit - */ - Period getPeriod(String path); - - /** - * Gets a value as a java.time.temporal.TemporalAmount. - * This method will first try get get the value as a java.time.Duration, and if unsuccessful, - * then as a java.time.Period. - * This means that values like "5m" will be parsed as 5 minutes rather than 5 months - * @param path path expression - * @return the temporal value at the requested path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to Long or String - * @throws com.gu.typesafe.config.ConfigException.BadValue - * if value cannot be parsed as a TemporalAmount - */ - TemporalAmount getTemporal(String path); - - /** - * Gets a list value (with any element type) as a {@link com.gu.typesafe.config.ConfigList}, which - * implements {@code java.util.List}. Throws if the path is - * unset or null. - * - * @param path - * the path to the list value. - * @return the {@link com.gu.typesafe.config.ConfigList} at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a ConfigList - */ - ConfigList getList(String path); - - /** - * Gets a list value with boolean elements. Throws if the - * path is unset or null or not a list or contains values not - * convertible to boolean. - * - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list of booleans - */ - List getBooleanList(String path); - - /** - * Gets a list value with number elements. Throws if the - * path is unset or null or not a list or contains values not - * convertible to number. - * - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list of numbers - */ - List getNumberList(String path); - - /** - * Gets a list value with int elements. Throws if the - * path is unset or null or not a list or contains values not - * convertible to int. - * - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list of ints - */ - List getIntList(String path); - - /** - * Gets a list value with long elements. Throws if the - * path is unset or null or not a list or contains values not - * convertible to long. - * - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list of longs - */ - List getLongList(String path); - - /** - * Gets a list value with double elements. Throws if the - * path is unset or null or not a list or contains values not - * convertible to double. - * - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list of doubles - */ - List getDoubleList(String path); - - /** - * Gets a list value with string elements. Throws if the - * path is unset or null or not a list or contains values not - * convertible to string. - * - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list of strings - */ - List getStringList(String path); - - /** - * Gets a list value with {@code Enum} elements. Throws if the - * path is unset or null or not a list or contains values not - * convertible to {@code Enum}. - * - * @param enumClass - * the enum class - * @param - * a generic denoting a specific type of enum - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list of {@code Enum} - */ - > List getEnumList(Class enumClass, String path); - - /** - * Gets a list value with object elements. Throws if the - * path is unset or null or not a list or contains values not - * convertible to ConfigObject. - * - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list of objects - */ - List getObjectList(String path); - - /** - * Gets a list value with Config elements. - * Throws if the path is unset or null or not a list or - * contains values not convertible to Config. - * - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list of configs - */ - List getConfigList(String path); - - /** - * Gets a list value with any kind of elements. Throws if the - * path is unset or null or not a list. Each element is - * "unwrapped" (see {@link com.gu.typesafe.config.ConfigValue#unwrapped()}). - * - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list - */ - List getAnyRefList(String path); - - /** - * Gets a list value with elements representing a size in - * bytes. Throws if the path is unset or null or not a list - * or contains values not convertible to memory sizes. - * - * @param path - * the path to the list value. - * @return the list at the path - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws com.gu.typesafe.config.ConfigException.WrongType - * if value is not convertible to a list of memory sizes - */ - List getBytesList(String path); - - /** - * Gets a list, converting each value in the list to a memory size, using the - * same rules as {@link #getMemorySize(String)}. - * - * @since 1.3.0 - * @param path - * a path expression - * @return list of memory sizes - * @throws com.gu.typesafe.config.ConfigException.Missing - * if value is absent or null - * @throws ConfigException.WrongType - * if value is not convertible to a list of memory sizes - */ - List getMemorySizeList(String path); - - /** - * @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)} - * @param path the path - * @return list of millisecond values - */ - @Deprecated List getMillisecondsList(String path); - - /** - * @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)} - * @param path the path - * @return list of nanosecond values - */ - @Deprecated List getNanosecondsList(String path); - - /** - * Gets a list, converting each value in the list to a duration, using the - * same rules as {@link #getDuration(String, TimeUnit)}. - * - * @since 1.2.0 - * @param path - * a path expression - * @param unit - * time units of the returned values - * @return list of durations, in the requested units - */ - List getDurationList(String path, TimeUnit unit); - - /** - * Gets a list, converting each value in the list to a duration, using the - * same rules as {@link #getDuration(String)}. - * - * @since 1.3.0 - * @param path - * a path expression - * @return list of durations - */ - List getDurationList(String path); - - /** - * Clone the config with only the given path (and its children) retained; - * all sibling paths are removed. - *

- * Note that path expressions have a syntax and sometimes require quoting - * (see {@link com.gu.typesafe.config.ConfigUtil#joinPath} and {@link com.gu.typesafe.config.ConfigUtil#splitPath}). - * - * @param path - * path to keep - * @return a copy of the config minus all paths except the one specified - */ - Config withOnlyPath(String path); - - /** - * Clone the config with the given path removed. - *

- * Note that path expressions have a syntax and sometimes require quoting - * (see {@link com.gu.typesafe.config.ConfigUtil#joinPath} and {@link com.gu.typesafe.config.ConfigUtil#splitPath}). - * - * @param path - * path expression to remove - * @return a copy of the config minus the specified path - */ - Config withoutPath(String path); - - /** - * Places the config inside another {@code Config} at the given path. - *

- * Note that path expressions have a syntax and sometimes require quoting - * (see {@link com.gu.typesafe.config.ConfigUtil#joinPath} and {@link com.gu.typesafe.config.ConfigUtil#splitPath}). - * - * @param path - * path expression to store this config at. - * @return a {@code Config} instance containing this config at the given - * path. - */ - Config atPath(String path); - - /** - * Places the config inside a {@code Config} at the given key. See also - * atPath(). Note that a key is NOT a path expression (see - * {@link com.gu.typesafe.config.ConfigUtil#joinPath} and {@link com.gu.typesafe.config.ConfigUtil#splitPath}). - * - * @param key - * key to store this config at. - * @return a {@code Config} instance containing this config at the given - * key. - */ - Config atKey(String key); - - /** - * Returns a {@code Config} based on this one, but with the given path set - * to the given value. Does not modify this instance (since it's immutable). - * If the path already has a value, that value is replaced. To remove a - * value, use withoutPath(). - *

- * Note that path expressions have a syntax and sometimes require quoting - * (see {@link com.gu.typesafe.config.ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}). - * - * @param path - * path expression for the value's new location - * @param value - * value at the new path - * @return the new instance with the new map entry - */ - Config withValue(String path, ConfigValue value); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigBeanFactory.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigBeanFactory.java deleted file mode 100644 index 6c7167bf1c..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigBeanFactory.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.gu.typesafe.config; - -import com.gu.typesafe.config.impl.ConfigBeanImpl; - -/** - * Factory for automatically creating a Java class from a {@link Config}. - * See {@link #create(Config,Class)}. - * - * @since 1.3.0 - */ -public class ConfigBeanFactory { - - /** - * Creates an instance of a class, initializing its fields from a {@link Config}. - * - * Example usage: - * - *

-     * Config configSource = ConfigFactory.load().getConfig("foo");
-     * FooConfig config = ConfigBeanFactory.create(configSource, FooConfig.class);
-     * 
- * - * The Java class should follow JavaBean conventions. Field types - * can be any of the types you can normally get from a {@link - * Config}, including java.time.Duration or {@link - * ConfigMemorySize}. Fields may also be another JavaBean-style - * class. - * - * Fields are mapped to config by converting the config key to - * camel case. So the key foo-bar becomes JavaBean - * setter setFooBar. - * - * @since 1.3.0 - * - * @param config source of config information - * @param clazz class to be instantiated - * @param the type of the class to be instantiated - * @return an instance of the class populated with data from the config - * @throws ConfigException.BadBean - * If something is wrong with the JavaBean - * @throws ConfigException.ValidationFailed - * If the config doesn't conform to the bean's implied schema - * @throws ConfigException - * Can throw the same exceptions as the getters on Config - */ - public static T create(Config config, Class clazz) { - return ConfigBeanImpl.createInternal(config, clazz); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigException.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigException.java deleted file mode 100644 index 57246930e2..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigException.java +++ /dev/null @@ -1,460 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.Field; - -import com.gu.typesafe.config.impl.ConfigImplUtil; - -/** - * All exceptions thrown by the library are subclasses of - * ConfigException. - */ -public abstract class ConfigException extends RuntimeException implements Serializable { - private static final long serialVersionUID = 1L; - - final private transient ConfigOrigin origin; - - protected ConfigException(ConfigOrigin origin, String message, - Throwable cause) { - super(origin.description() + ": " + message, cause); - this.origin = origin; - } - - protected ConfigException(ConfigOrigin origin, String message) { - this(origin.description() + ": " + message, null); - } - - protected ConfigException(String message, Throwable cause) { - super(message, cause); - this.origin = null; - } - - protected ConfigException(String message) { - this(message, null); - } - - /** - * Returns an "origin" (such as a filename and line number) for the - * exception, or null if none is available. If there's no sensible origin - * for a given exception, or the kind of exception doesn't meaningfully - * relate to a particular origin file, this returns null. Never assume this - * will return non-null, it can always return null. - * - * @return origin of the problem, or null if unknown/inapplicable - */ - public ConfigOrigin origin() { - return origin; - } - - // we customize serialization because ConfigOrigin isn't - // serializable and we don't want it to be (don't want to - // support it) - private void writeObject(java.io.ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - ConfigImplUtil.writeOrigin(out, origin); - } - - // For deserialization - uses reflection to set the final origin field on the object - private static void setOriginField(T hasOriginField, Class clazz, - ConfigOrigin origin) throws IOException { - // circumvent "final" - Field f; - try { - f = clazz.getDeclaredField("origin"); - } catch (NoSuchFieldException e) { - throw new IOException(clazz.getSimpleName() + " has no origin field?", e); - } catch (SecurityException e) { - throw new IOException("unable to fill out origin field in " + - clazz.getSimpleName(), e); - } - f.setAccessible(true); - try { - f.set(hasOriginField, origin); - } catch (IllegalArgumentException e) { - throw new IOException("unable to set origin field", e); - } catch (IllegalAccessException e) { - throw new IOException("unable to set origin field", e); - } - } - - private void readObject(java.io.ObjectInputStream in) throws IOException, - ClassNotFoundException { - in.defaultReadObject(); - ConfigOrigin origin = ConfigImplUtil.readOrigin(in); - setOriginField(this, ConfigException.class, origin); - } - - /** - * Exception indicating that the type of a value does not match the type you - * requested. - * - */ - public static class WrongType extends ConfigException { - private static final long serialVersionUID = 1L; - - public WrongType(ConfigOrigin origin, String path, String expected, String actual, - Throwable cause) { - super(origin, path + " has type " + actual + " rather than " + expected, cause); - } - - public WrongType(ConfigOrigin origin, String path, String expected, String actual) { - this(origin, path, expected, actual, null); - } - - public WrongType(ConfigOrigin origin, String message, Throwable cause) { - super(origin, message, cause); - } - - public WrongType(ConfigOrigin origin, String message) { - super(origin, message, null); - } - } - - /** - * Exception indicates that the setting was never set to anything, not even - * null. - */ - public static class Missing extends ConfigException { - private static final long serialVersionUID = 1L; - - public Missing(String path, Throwable cause) { - super("No configuration setting found for key '" + path + "'", - cause); - } - - public Missing(ConfigOrigin origin, String path) { - this(origin, "No configuration setting found for key '" + path + "'", null); - } - - public Missing(String path) { - this(path, null); - } - - protected Missing(ConfigOrigin origin, String message, Throwable cause) { - super(origin, message, cause); - } - - } - - /** - * Exception indicates that the setting was treated as missing because it - * was set to null. - */ - public static class Null extends Missing { - private static final long serialVersionUID = 1L; - - private static String makeMessage(String path, String expected) { - if (expected != null) { - return "Configuration key '" + path - + "' is set to null but expected " + expected; - } else { - return "Configuration key '" + path + "' is null"; - } - } - - public Null(ConfigOrigin origin, String path, String expected, - Throwable cause) { - super(origin, makeMessage(path, expected), cause); - } - - public Null(ConfigOrigin origin, String path, String expected) { - this(origin, path, expected, null); - } - } - - /** - * Exception indicating that a value was messed up, for example you may have - * asked for a duration and the value can't be sensibly parsed as a - * duration. - * - */ - public static class BadValue extends ConfigException { - private static final long serialVersionUID = 1L; - - public BadValue(ConfigOrigin origin, String path, String message, - Throwable cause) { - super(origin, "Invalid value at '" + path + "': " + message, cause); - } - - public BadValue(ConfigOrigin origin, String path, String message) { - this(origin, path, message, null); - } - - public BadValue(String path, String message, Throwable cause) { - super("Invalid value at '" + path + "': " + message, cause); - } - - public BadValue(String path, String message) { - this(path, message, null); - } - } - - /** - * Exception indicating that a path expression was invalid. Try putting - * double quotes around path elements that contain "special" characters. - * - */ - public static class BadPath extends ConfigException { - private static final long serialVersionUID = 1L; - - public BadPath(ConfigOrigin origin, String path, String message, - Throwable cause) { - super(origin, - path != null ? ("Invalid path '" + path + "': " + message) - : message, cause); - } - - public BadPath(ConfigOrigin origin, String path, String message) { - this(origin, path, message, null); - } - - public BadPath(String path, String message, Throwable cause) { - super(path != null ? ("Invalid path '" + path + "': " + message) - : message, cause); - } - - public BadPath(String path, String message) { - this(path, message, null); - } - - public BadPath(ConfigOrigin origin, String message) { - this(origin, null, message); - } - } - - /** - * Exception indicating that there's a bug in something (possibly the - * library itself) or the runtime environment is broken. This exception - * should never be handled; instead, something should be fixed to keep the - * exception from occurring. This exception can be thrown by any method in - * the library. - */ - public static class BugOrBroken extends ConfigException { - private static final long serialVersionUID = 1L; - - public BugOrBroken(String message, Throwable cause) { - super(message, cause); - } - - public BugOrBroken(String message) { - this(message, null); - } - } - - /** - * Exception indicating that there was an IO error. - * - */ - public static class IO extends ConfigException { - private static final long serialVersionUID = 1L; - - public IO(ConfigOrigin origin, String message, Throwable cause) { - super(origin, message, cause); - } - - public IO(ConfigOrigin origin, String message) { - this(origin, message, null); - } - } - - /** - * Exception indicating that there was a parse error. - * - */ - public static class Parse extends ConfigException { - private static final long serialVersionUID = 1L; - - public Parse(ConfigOrigin origin, String message, Throwable cause) { - super(origin, message, cause); - } - - public Parse(ConfigOrigin origin, String message) { - this(origin, message, null); - } - } - - /** - * Exception indicating that a substitution did not resolve to anything. - * Thrown by {@link Config#resolve}. - */ - public static class UnresolvedSubstitution extends Parse { - private static final long serialVersionUID = 1L; - - private final String detail; - - public UnresolvedSubstitution(ConfigOrigin origin, String detail, Throwable cause) { - super(origin, "Could not resolve substitution to a value: " + detail, cause); - this.detail = detail; - } - - public UnresolvedSubstitution(ConfigOrigin origin, String detail) { - this(origin, detail, null); - } - - private UnresolvedSubstitution(UnresolvedSubstitution wrapped, ConfigOrigin origin, String message) { - super(origin, message, wrapped); - this.detail = wrapped.detail; - } - - public UnresolvedSubstitution addExtraDetail(String extra) { - return new UnresolvedSubstitution(this, origin(), String.format(extra, detail)); - } - } - - /** - * Exception indicating that you tried to use a function that requires - * substitutions to be resolved, but substitutions have not been resolved - * (that is, {@link Config#resolve} was not called). This is always a bug in - * either application code or the library; it's wrong to write a handler for - * this exception because you should be able to fix the code to avoid it by - * adding calls to {@link Config#resolve}. - */ - public static class NotResolved extends BugOrBroken { - private static final long serialVersionUID = 1L; - - public NotResolved(String message, Throwable cause) { - super(message, cause); - } - - public NotResolved(String message) { - this(message, null); - } - } - - /** - * Information about a problem that occurred in {@link Config#checkValid}. A - * {@link ConfigException.ValidationFailed} exception thrown from - * checkValid() includes a list of problems encountered. - */ - public static class ValidationProblem implements Serializable { - - final private String path; - final private transient ConfigOrigin origin; - final private String problem; - - public ValidationProblem(String path, ConfigOrigin origin, String problem) { - this.path = path; - this.origin = origin; - this.problem = problem; - } - - /** - * Returns the config setting causing the problem. - * @return the path of the problem setting - */ - public String path() { - return path; - } - - /** - * Returns where the problem occurred (origin may include info on the - * file, line number, etc.). - * @return the origin of the problem setting - */ - public ConfigOrigin origin() { - return origin; - } - - /** - * Returns a description of the problem. - * @return description of the problem - */ - public String problem() { - return problem; - } - - // We customize serialization because ConfigOrigin isn't - // serializable and we don't want it to be - private void writeObject(java.io.ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - ConfigImplUtil.writeOrigin(out, origin); - } - - private void readObject(java.io.ObjectInputStream in) throws IOException, - ClassNotFoundException { - in.defaultReadObject(); - ConfigOrigin origin = ConfigImplUtil.readOrigin(in); - setOriginField(this, ValidationProblem.class, origin); - } - - @Override - public String toString() { - return "ValidationProblem(" + path + "," + origin + "," + problem + ")"; - } - } - - /** - * Exception indicating that {@link Config#checkValid} found validity - * problems. The problems are available via the {@link #problems()} method. - * The getMessage() of this exception is a potentially very - * long string listing all the problems found. - */ - public static class ValidationFailed extends ConfigException { - private static final long serialVersionUID = 1L; - - final private Iterable problems; - - public ValidationFailed(Iterable problems) { - super(makeMessage(problems), null); - this.problems = problems; - } - - public Iterable problems() { - return problems; - } - - private static String makeMessage(Iterable problems) { - StringBuilder sb = new StringBuilder(); - for (ValidationProblem p : problems) { - sb.append(p.origin().description()); - sb.append(": "); - sb.append(p.path()); - sb.append(": "); - sb.append(p.problem()); - sb.append(", "); - } - if (sb.length() == 0) - throw new ConfigException.BugOrBroken( - "ValidationFailed must have a non-empty list of problems"); - sb.setLength(sb.length() - 2); // chop comma and space - - return sb.toString(); - } - } - - /** - * Some problem with a JavaBean we are trying to initialize. - * @since 1.3.0 - */ - public static class BadBean extends BugOrBroken { - private static final long serialVersionUID = 1L; - - public BadBean(String message, Throwable cause) { - super(message, cause); - } - - public BadBean(String message) { - this(message, null); - } - } - - /** - * Exception that doesn't fall into any other category. - */ - public static class Generic extends ConfigException { - private static final long serialVersionUID = 1L; - - public Generic(String message, Throwable cause) { - super(message, cause); - } - - public Generic(String message) { - this(message, null); - } - } - -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigFactory.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigFactory.java deleted file mode 100644 index 093170f284..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigFactory.java +++ /dev/null @@ -1,1258 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -import com.gu.typesafe.config.impl.ConfigImpl; -import com.gu.typesafe.config.impl.Parseable; - -import java.io.File; -import java.io.Reader; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.concurrent.Callable; - -/** - * Contains static methods for creating {@link Config} instances. - * - *

- * See also {@link com.gu.typesafe.config.ConfigValueFactory} which contains static methods for - * converting Java values into a {@link ConfigObject}. You can then convert a - * {@code ConfigObject} into a {@code Config} with {@link ConfigObject#toConfig}. - * - *

- * The static methods with "load" in the name do some sort of higher-level - * operation potentially parsing multiple resources and resolving substitutions, - * while the ones with "parse" in the name just create a {@link ConfigValue} - * from a resource and nothing else. - * - *

You can find an example app and library on - * GitHub. Also be sure to read the package - * overview which describes the big picture as shown in those - * examples. - */ -public final class ConfigFactory { - private static final String STRATEGY_PROPERTY_NAME = "config.strategy"; - private static final String OVERRIDE_WITH_ENV_PROPERTY_NAME = "config.override_with_env_vars"; - - private ConfigFactory() { - } - - /** - * Loads an application's configuration from the given classpath resource or - * classpath resource basename, sandwiches it between default reference - * config and default overrides, and then resolves it. The classpath - * resource is "raw" (it should have no "/" prefix, and is not made relative - * to any package, so it's like {@link ClassLoader#getResource} not - * {@link Class#getResource}). - * - *

- * Resources are loaded from the current thread's - * {@link Thread#getContextClassLoader()}. In general, a library needs its - * configuration to come from the class loader used to load that library, so - * the proper "reference.conf" are present. - * - *

- * The loaded object will already be resolved (substitutions have already - * been processed). As a result, if you add more fallbacks then they won't - * be seen by substitutions. Substitutions are the "${foo.bar}" syntax. If - * you want to parse additional files or something then you need to use - * {@link #load(Config)}. - * - *

- * To load a standalone resource (without the default reference and default - * overrides), use {@link #parseResourcesAnySyntax(String)} rather than this - * method. To load only the reference config use {@link #defaultReference()} - * and to load only the overrides use {@link #defaultOverrides()}. - * - * @param resourceBasename - * name (optionally without extension) of a resource on classpath - * @return configuration for an application relative to context class loader - */ - public static Config load(String resourceBasename) { - return load(resourceBasename, ConfigParseOptions.defaults(), - com.gu.typesafe.config.ConfigResolveOptions.defaults()); - } - - /** - * Like {@link #load(String)} but uses the supplied class loader instead of - * the current thread's context class loader. - * - *

- * To load a standalone resource (without the default reference and default - * overrides), use {@link #parseResourcesAnySyntax(ClassLoader, String)} - * rather than this method. To load only the reference config use - * {@link #defaultReference(ClassLoader)} and to load only the overrides use - * {@link #defaultOverrides(ClassLoader)}. - * - * @param loader class loader to look for resources in - * @param resourceBasename basename (no .conf/.json/.properties suffix) - * @return configuration for an application relative to given class loader - */ - public static Config load(ClassLoader loader, String resourceBasename) { - return load(resourceBasename, ConfigParseOptions.defaults().setClassLoader(loader), - com.gu.typesafe.config.ConfigResolveOptions.defaults()); - } - - /** - * Like {@link #load(String)} but allows you to specify parse and resolve - * options. - * - * @param resourceBasename - * the classpath resource name with optional extension - * @param parseOptions - * options to use when parsing the resource - * @param resolveOptions - * options to use when resolving the stack - * @return configuration for an application - */ - public static Config load(String resourceBasename, ConfigParseOptions parseOptions, - com.gu.typesafe.config.ConfigResolveOptions resolveOptions) { - ConfigParseOptions withLoader = ensureClassLoader(parseOptions, "load"); - Config appConfig = ConfigFactory.parseResourcesAnySyntax(resourceBasename, withLoader); - return load(withLoader.getClassLoader(), appConfig, resolveOptions); - } - - /** - * Like {@link #load(String,ConfigParseOptions, com.gu.typesafe.config.ConfigResolveOptions)} but - * has a class loader parameter that overrides any from the - * {@code ConfigParseOptions}. - * - * @param loader - * class loader in which to find resources (overrides loader in - * parse options) - * @param resourceBasename - * the classpath resource name with optional extension - * @param parseOptions - * options to use when parsing the resource (class loader - * overridden) - * @param resolveOptions - * options to use when resolving the stack - * @return configuration for an application - */ - public static Config load(ClassLoader loader, String resourceBasename, - ConfigParseOptions parseOptions, com.gu.typesafe.config.ConfigResolveOptions resolveOptions) { - return load(resourceBasename, parseOptions.setClassLoader(loader), resolveOptions); - } - - private static ClassLoader checkedContextClassLoader(String methodName) { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - if (loader == null) - throw new ConfigException.BugOrBroken("Context class loader is not set for the current thread; " - + "if Thread.currentThread().getContextClassLoader() returns null, you must pass a ClassLoader " - + "explicitly to ConfigFactory." + methodName); - else - return loader; - } - - private static ConfigParseOptions ensureClassLoader(ConfigParseOptions options, String methodName) { - if (options.getClassLoader() == null) - return options.setClassLoader(checkedContextClassLoader(methodName)); - else - return options; - } - - /** - * Assembles a standard configuration using a custom Config - * object rather than loading "application.conf". The Config - * object will be sandwiched between the default reference config and - * default overrides and then resolved. - * - * @param config - * the application's portion of the configuration - * @return resolved configuration with overrides and fallbacks added - */ - public static Config load(Config config) { - return load(checkedContextClassLoader("load"), config); - } - - /** - * Like {@link #load(Config)} but allows you to specify - * the class loader for looking up resources. - * - * @param loader - * the class loader to use to find resources - * @param config - * the application's portion of the configuration - * @return resolved configuration with overrides and fallbacks added - */ - public static Config load(ClassLoader loader, Config config) { - return load(loader, config, com.gu.typesafe.config.ConfigResolveOptions.defaults()); - } - - /** - * Like {@link #load(Config)} but allows you to specify - * {@link com.gu.typesafe.config.ConfigResolveOptions}. - * - * @param config - * the application's portion of the configuration - * @param resolveOptions - * options for resolving the assembled config stack - * @return resolved configuration with overrides and fallbacks added - */ - public static Config load(Config config, com.gu.typesafe.config.ConfigResolveOptions resolveOptions) { - return load(checkedContextClassLoader("load"), config, resolveOptions); - } - - /** - * Like {@link #load(Config, com.gu.typesafe.config.ConfigResolveOptions)} but allows you to specify - * a class loader other than the context class loader. - * - * @param loader - * class loader to use when looking up override and reference - * configs - * @param config - * the application's portion of the configuration - * @param resolveOptions - * options for resolving the assembled config stack - * @return resolved configuration with overrides and fallbacks added - */ - public static Config load(ClassLoader loader, Config config, com.gu.typesafe.config.ConfigResolveOptions resolveOptions) { - return defaultOverrides(loader).withFallback(config) - .withFallback(ConfigImpl.defaultReferenceUnresolved(loader)) - .resolve(resolveOptions); - } - - - - /** - * Loads a default configuration, equivalent to {@link #load(Config) - * load(defaultApplication())} in most cases. This configuration should be used by - * libraries and frameworks unless an application provides a different one. - *

- * This method may return a cached singleton so will not see changes to - * system properties or config files. (Use {@link #invalidateCaches()} to - * force it to reload.) - * - * @return configuration for an application - */ - public static Config load() { - ClassLoader loader = checkedContextClassLoader("load"); - return load(loader); - } - - /** - * Like {@link #load()} but allows specifying parse options. - * - * @param parseOptions - * Options for parsing resources - * @return configuration for an application - */ - public static Config load(ConfigParseOptions parseOptions) { - return load(parseOptions, com.gu.typesafe.config.ConfigResolveOptions.defaults()); - } - - /** - * Like {@link #load()} but allows specifying a class loader other than the - * thread's current context class loader. - * - * @param loader - * class loader for finding resources - * @return configuration for an application - */ - public static Config load(final ClassLoader loader) { - final ConfigParseOptions withLoader = ConfigParseOptions.defaults().setClassLoader(loader); - return ConfigImpl.computeCachedConfig(loader, "load", new Callable() { - @Override - public Config call() { - return load(loader, defaultApplication(withLoader)); - } - }); - } - - /** - * Like {@link #load()} but allows specifying a class loader other than the - * thread's current context class loader and also specify parse options. - * - * @param loader - * class loader for finding resources (overrides any loader in parseOptions) - * @param parseOptions - * Options for parsing resources - * @return configuration for an application - */ - public static Config load(ClassLoader loader, ConfigParseOptions parseOptions) { - return load(parseOptions.setClassLoader(loader)); - } - - /** - * Like {@link #load()} but allows specifying a class loader other than the - * thread's current context class loader and also specify resolve options. - * - * @param loader - * class loader for finding resources - * @param resolveOptions - * options for resolving the assembled config stack - * @return configuration for an application - */ - public static Config load(ClassLoader loader, com.gu.typesafe.config.ConfigResolveOptions resolveOptions) { - return load(loader, ConfigParseOptions.defaults(), resolveOptions); - } - - - /** - * Like {@link #load()} but allows specifying a class loader other than the - * thread's current context class loader, parse options, and resolve options. - * - * @param loader - * class loader for finding resources (overrides any loader in parseOptions) - * @param parseOptions - * Options for parsing resources - * @param resolveOptions - * options for resolving the assembled config stack - * @return configuration for an application - */ - public static Config load(ClassLoader loader, ConfigParseOptions parseOptions, com.gu.typesafe.config.ConfigResolveOptions resolveOptions) { - final ConfigParseOptions withLoader = ensureClassLoader(parseOptions, "load"); - return load(loader, defaultApplication(withLoader), resolveOptions); - } - - /** - * Like {@link #load()} but allows specifying parse options and resolve - * options. - * - * @param parseOptions - * Options for parsing resources - * @param resolveOptions - * options for resolving the assembled config stack - * @return configuration for an application - * - * @since 1.3.0 - */ - public static Config load(ConfigParseOptions parseOptions, final ConfigResolveOptions resolveOptions) { - final ConfigParseOptions withLoader = ensureClassLoader(parseOptions, "load"); - return load(defaultApplication(withLoader), resolveOptions); - } - - /** - * Obtains the default reference configuration, which is currently created - * by merging all resources "reference.conf" found on the classpath and - * overriding the result with system properties. The returned reference - * configuration will already have substitutions resolved. - * - *

- * Libraries and frameworks should ship with a "reference.conf" in their - * jar. - * - *

- * The reference config must be looked up in the class loader that contains - * the libraries that you want to use with this config, so the - * "reference.conf" for each library can be found. Use - * {@link #defaultReference(ClassLoader)} if the context class loader is not - * suitable. - * - *

- * The {@link #load()} methods merge this configuration for you - * automatically. - * - *

- * Future versions may look for reference configuration in more places. It - * is not guaranteed that this method only looks at - * "reference.conf". - * - * @return the default reference config for context class loader - */ - public static Config defaultReference() { - return defaultReference(checkedContextClassLoader("defaultReference")); - } - - /** - * Like {@link #defaultReference()} but allows you to specify a class loader - * to use rather than the current context class loader. - * - * @param loader class loader to look for resources in - * @return the default reference config for this class loader - */ - public static Config defaultReference(ClassLoader loader) { - return ConfigImpl.defaultReference(loader); - } - - /** - * Obtains the default reference configuration, which is currently created - * by merging all resources "reference.conf" found on the classpath and - * overriding the result with system properties. - * - *

- * While the returned reference configuration is guaranteed to be - * resolvable (that is, there will be no substitutions that cannot be - * resolved), it is returned in an unresolved state for the purpose of - * allowing substitutions to be overridden by a config layer that falls - * back to this one. - * - *

- * Libraries and frameworks should ship with a "reference.conf" in their - * jar. - * - *

- * The reference config must be looked up in the class loader that contains - * the libraries that you want to use with this config, so the - * "reference.conf" for each library can be found. Use - * {@link #defaultReference(ClassLoader)} if the context class loader is not - * suitable. - * - *

- * The {@link #load()} methods merge this configuration for you - * automatically. - * - *

- * Future versions may look for reference configuration in more places. It - * is not guaranteed that this method only looks at - * "reference.conf". - * - * @return the unresolved default reference config for the context class - * loader - */ - public static Config defaultReferenceUnresolved() { - return defaultReferenceUnresolved(checkedContextClassLoader("defaultReferenceUnresolved")); - } - - /** - * Like {@link #defaultReferenceUnresolved()} but allows you to specify a - * class loader to use rather than the current context class loader. - * - * @param loader class loader to look for resources in - * @return the unresolved default reference config for this class loader - */ - public static Config defaultReferenceUnresolved(ClassLoader loader) { - return ConfigImpl.defaultReferenceUnresolved(loader); - } - - /** - * Obtains the default override configuration, which currently consists of - * system properties. The returned override configuration will already have - * substitutions resolved. - * - *

- * The {@link #load()} methods merge this configuration for you - * automatically. - * - *

- * Future versions may get overrides in more places. It is not guaranteed - * that this method only uses system properties. - * - * @return the default override configuration - */ - public static Config defaultOverrides() { - if (getOverrideWithEnv()) { - return systemEnvironmentOverrides().withFallback(systemProperties()); - } else { - return systemProperties(); - } - } - - /** - * Like {@link #defaultOverrides()} but allows you to specify a class loader - * to use rather than the current context class loader. - * - * @param loader class loader to look for resources in - * @return the default override configuration - */ - public static Config defaultOverrides(ClassLoader loader) { - return defaultOverrides(); - } - - /** - * Obtains the default application-specific configuration, - * which defaults to parsing application.conf, - * application.json, and - * application.properties on the classpath, but - * can also be rerouted using the config.file, - * config.resource, and config.url - * system properties. - * - *

The no-arguments {@link #load()} method automatically - * stacks the {@link #defaultReference()}, {@link - * #defaultApplication()}, and {@link #defaultOverrides()} - * configs. You would use defaultApplication() - * directly only if you're somehow customizing behavior by - * reimplementing load(). - * - *

The configuration returned by - * defaultApplication() will not be resolved - * already, in contrast to defaultReference() and - * defaultOverrides(). This is because - * application.conf would normally be resolved after - * merging with the reference and override configs. - * - *

- * If the system properties config.resource, - * config.file, or config.url are set, then the - * classpath resource, file, or URL specified in those properties will be - * used rather than the default - * application.{conf,json,properties} classpath resources. - * These system properties should not be set in code (after all, you can - * just parse whatever you want manually and then use {@link #load(Config)} - * if you don't want to use application.conf). The properties - * are intended for use by the person or script launching the application. - * For example someone might have a production.conf that - * include application.conf but then change a couple of values. - * When launching the app they could specify - * -Dconfig.resource=production.conf to get production mode. - * - *

- * If no system properties are set to change the location of the default - * configuration, defaultApplication() is equivalent to - * ConfigFactory.parseResources("application"). - * - * @since 1.3.0 - * - * @return the default application.conf or system-property-configured configuration - */ - public static Config defaultApplication() { - return defaultApplication(ConfigParseOptions.defaults()); - } - - /** - * Like {@link #defaultApplication()} but allows you to specify a class loader - * to use rather than the current context class loader. - * - * @since 1.3.0 - * - * @param loader class loader to look for resources in - * @return the default application configuration - */ - public static Config defaultApplication(ClassLoader loader) { - return defaultApplication(ConfigParseOptions.defaults().setClassLoader(loader)); - } - - /** - * Like {@link #defaultApplication()} but allows you to specify parse options. - * - * @since 1.3.0 - * - * @param options the options - * @return the default application configuration - */ - public static Config defaultApplication(ConfigParseOptions options) { - return getConfigLoadingStrategy().parseApplicationConfig(ensureClassLoader(options, "defaultApplication")); - } - - /** - * Reloads any cached configs, picking up changes to system properties for - * example. Because a {@link Config} is immutable, anyone with a reference - * to the old configs will still have the same outdated objects. However, - * new calls to {@link #load()} or {@link #defaultOverrides()} or - * {@link #defaultReference} may return a new object. - *

- * This method is primarily intended for use in unit tests, for example, - * that may want to update a system property then confirm that it's used - * correctly. In many cases, use of this method may indicate there's a - * better way to set up your code. - *

- * Caches may be reloaded immediately or lazily; once you call this method, - * the reload can occur at any time, even during the invalidation process. - * So FIRST make the changes you'd like the caches to notice, then SECOND - * call this method to invalidate caches. Don't expect that invalidating, - * making changes, then calling {@link #load()}, will work. Make changes - * before you invalidate. - */ - public static void invalidateCaches() { - // We rely on this having the side effect that it drops - // all caches - ConfigImpl.reloadSystemPropertiesConfig(); - ConfigImpl.reloadEnvVariablesConfig(); - ConfigImpl.reloadEnvVariablesOverridesConfig(); - } - - /** - * Gets an empty configuration. See also {@link #empty(String)} to create an - * empty configuration with a description, which may improve user-visible - * error messages. - * - * @return an empty configuration - */ - public static Config empty() { - return empty(null); - } - - /** - * Gets an empty configuration with a description to be used to create a - * {@link ConfigOrigin} for this Config. The description should - * be very short and say what the configuration is, like "default settings" - * or "foo settings" or something. (Presumably you will merge some actual - * settings into this empty config using {@link Config#withFallback}, making - * the description more useful.) - * - * @param originDescription - * description of the config - * @return an empty configuration - */ - public static Config empty(String originDescription) { - return ConfigImpl.emptyConfig(originDescription); - } - - /** - * Gets a Config containing the system properties from - * {@link java.lang.System#getProperties()}, parsed and converted as with - * {@link #parseProperties}. - *

- * This method can return a global immutable singleton, so it's preferred - * over parsing system properties yourself. - *

- * {@link #load} will include the system properties as overrides already, as - * will {@link #defaultReference} and {@link #defaultOverrides}. - * - *

- * Because this returns a singleton, it will not notice changes to system - * properties made after the first time this method is called. Use - * {@link #invalidateCaches()} to force the singleton to reload if you - * modify system properties. - * - * @return system properties parsed into a Config - */ - public static Config systemProperties() { - return ConfigImpl.systemPropertiesAsConfig(); - } - - /** - * Gets a Config containing the system's environment variables - * used to override configuration keys. - * Environment variables taken in considerations are starting with - * {@code CONFIG_FORCE_} - * - *

- * Environment variables are mangled in the following way after stripping the prefix "CONFIG_FORCE_": - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Env VarConfig
_   [1 underscore]. [dot]
__  [2 underscore]- [dash]
___ [3 underscore]_ [underscore]
- * - *

- * A variable like: {@code CONFIG_FORCE_a_b__c___d} - * is translated to a config key: {@code a.b-c_d} - * - *

- * This method can return a global immutable singleton, so it's preferred - * over parsing system properties yourself. - *

- * {@link #defaultOverrides} will include the system environment variables as - * overrides if `config.override_with_env_vars` is set to `true`. - * - * @return system environment variable overrides parsed into a Config - */ - public static Config systemEnvironmentOverrides() { - return ConfigImpl.envVariablesOverridesAsConfig(); - } - - /** - * Gets a Config containing the system's environment variables. - * This method can return a global immutable singleton. - * - *

- * Environment variables are used as fallbacks when resolving substitutions - * whether or not this object is included in the config being resolved, so - * you probably don't need to use this method for most purposes. It can be a - * nicer API for accessing environment variables than raw - * {@link java.lang.System#getenv(String)} though, since you can use methods - * such as {@link Config#getInt}. - * - * @return system environment variables parsed into a Config - */ - public static Config systemEnvironment() { - return ConfigImpl.envVariablesAsConfig(); - } - - /** - * Converts a Java {@link java.util.Properties} object to a - * {@link ConfigObject} using the rules documented in the HOCON - * spec. The keys in the Properties object are split on the - * period character '.' and treated as paths. The values will all end up as - * string values. If you have both "a=foo" and "a.b=bar" in your properties - * file, so "a" is both the object containing "b" and the string "foo", then - * the string value is dropped. - * - *

- * If you want to have System.getProperties() as a - * ConfigObject, it's better to use the {@link #systemProperties()} method - * which returns a cached global singleton. - * - * @param properties - * a Java Properties object - * @param options - * the parse options - * @return the parsed configuration - */ - public static Config parseProperties(Properties properties, - ConfigParseOptions options) { - return Parseable.newProperties(properties, options).parse().toConfig(); - } - - /** - * Like {@link #parseProperties(Properties, ConfigParseOptions)} but uses default - * parse options. - * @param properties - * a Java Properties object - * @return the parsed configuration - */ - public static Config parseProperties(Properties properties) { - return parseProperties(properties, ConfigParseOptions.defaults()); - } - - /** - * Parses a Reader into a Config instance. Does not call - * {@link Config#resolve} or merge the parsed stream with any - * other configuration; this method parses a single stream and - * does nothing else. It does process "include" statements in - * the parsed stream, and may end up doing other IO due to those - * statements. - * - * @param reader - * the reader to parse - * @param options - * parse options to control how the reader is interpreted - * @return the parsed configuration - * @throws ConfigException on IO or parse errors - */ - public static Config parseReader(Reader reader, ConfigParseOptions options) { - return Parseable.newReader(reader, options).parse().toConfig(); - } - - /** - * Parses a reader into a Config instance as with - * {@link #parseReader(Reader,ConfigParseOptions)} but always uses the - * default parse options. - * - * @param reader - * the reader to parse - * @return the parsed configuration - * @throws ConfigException on IO or parse errors - */ - public static Config parseReader(Reader reader) { - return parseReader(reader, ConfigParseOptions.defaults()); - } - - /** - * Parses a URL into a Config instance. Does not call - * {@link Config#resolve} or merge the parsed stream with any - * other configuration; this method parses a single stream and - * does nothing else. It does process "include" statements in - * the parsed stream, and may end up doing other IO due to those - * statements. - * - * @param url - * the url to parse - * @param options - * parse options to control how the url is interpreted - * @return the parsed configuration - * @throws ConfigException on IO or parse errors - */ - public static Config parseURL(URL url, ConfigParseOptions options) { - return Parseable.newURL(url, options).parse().toConfig(); - } - - /** - * Parses a url into a Config instance as with - * {@link #parseURL(URL,ConfigParseOptions)} but always uses the - * default parse options. - * - * @param url - * the url to parse - * @return the parsed configuration - * @throws ConfigException on IO or parse errors - */ - public static Config parseURL(URL url) { - return parseURL(url, ConfigParseOptions.defaults()); - } - - /** - * Parses a file into a Config instance. Does not call - * {@link Config#resolve} or merge the file with any other - * configuration; this method parses a single file and does - * nothing else. It does process "include" statements in the - * parsed file, and may end up doing other IO due to those - * statements. - * - * @param file - * the file to parse - * @param options - * parse options to control how the file is interpreted - * @return the parsed configuration - * @throws ConfigException on IO or parse errors - */ - public static Config parseFile(File file, ConfigParseOptions options) { - return Parseable.newFile(file, options).parse().toConfig(); - } - - /** - * Parses a file into a Config instance as with - * {@link #parseFile(File,ConfigParseOptions)} but always uses the - * default parse options. - * - * @param file - * the file to parse - * @return the parsed configuration - * @throws ConfigException on IO or parse errors - */ - public static Config parseFile(File file) { - return parseFile(file, ConfigParseOptions.defaults()); - } - - /** - * Parses a file with a flexible extension. If the fileBasename - * already ends in a known extension, this method parses it according to - * that extension (the file's syntax must match its extension). If the - * fileBasename does not end in an extension, it parses files - * with all known extensions and merges whatever is found. - * - *

- * In the current implementation, the extension ".conf" forces - * {@link ConfigSyntax#CONF}, ".json" forces {@link ConfigSyntax#JSON}, and - * ".properties" forces {@link ConfigSyntax#PROPERTIES}. When merging files, - * ".conf" falls back to ".json" falls back to ".properties". - * - *

- * Future versions of the implementation may add additional syntaxes or - * additional extensions. However, the ordering (fallback priority) of the - * three current extensions will remain the same. - * - *

- * If options forces a specific syntax, this method only parses - * files with an extension matching that syntax. - * - *

- * If {@link ConfigParseOptions#getAllowMissing options.getAllowMissing()} - * is true, then no files have to exist; if false, then at least one file - * has to exist. - * - * @param fileBasename - * a filename with or without extension - * @param options - * parse options - * @return the parsed configuration - */ - public static Config parseFileAnySyntax(File fileBasename, - ConfigParseOptions options) { - return ConfigImpl.parseFileAnySyntax(fileBasename, options).toConfig(); - } - - /** - * Like {@link #parseFileAnySyntax(File,ConfigParseOptions)} but always uses - * default parse options. - * - * @param fileBasename - * a filename with or without extension - * @return the parsed configuration - */ - public static Config parseFileAnySyntax(File fileBasename) { - return parseFileAnySyntax(fileBasename, ConfigParseOptions.defaults()); - } - - /** - * Parses all resources on the classpath with the given name and merges them - * into a single Config. - * - *

- * If the resource name does not begin with a "/", it will have the supplied - * class's package added to it, in the same way as - * {@link java.lang.Class#getResource}. - * - *

- * Duplicate resources with the same name are merged such that ones returned - * earlier from {@link ClassLoader#getResources} fall back to (have higher - * priority than) the ones returned later. This implies that resources - * earlier in the classpath override those later in the classpath when they - * configure the same setting. However, in practice real applications may - * not be consistent about classpath ordering, so be careful. It may be best - * to avoid assuming too much. - * - * @param klass - * klass.getClassLoader() will be used to load - * resources, and non-absolute resource names will have this - * class's package added - * @param resource - * resource to look up, relative to klass's package - * or absolute starting with a "/" - * @param options - * parse options - * @return the parsed configuration - */ - public static Config parseResources(Class klass, String resource, - ConfigParseOptions options) { - return Parseable.newResources(klass, resource, options).parse() - .toConfig(); - } - - /** - * Like {@link #parseResources(Class,String,ConfigParseOptions)} but always uses - * default parse options. - * - * @param klass - * klass.getClassLoader() will be used to load - * resources, and non-absolute resource names will have this - * class's package added - * @param resource - * resource to look up, relative to klass's package - * or absolute starting with a "/" - * @return the parsed configuration - */ - public static Config parseResources(Class klass, String resource) { - return parseResources(klass, resource, ConfigParseOptions.defaults()); - } - - /** - * Parses classpath resources with a flexible extension. In general, this - * method has the same behavior as - * {@link #parseFileAnySyntax(File,ConfigParseOptions)} but for classpath - * resources instead, as in {@link #parseResources}. - * - *

- * There is a thorny problem with this method, which is that - * {@link java.lang.ClassLoader#getResources} must be called separately for - * each possible extension. The implementation ends up with separate lists - * of resources called "basename.conf" and "basename.json" for example. As a - * result, the ideal ordering between two files with different extensions is - * unknown; there is no way to figure out how to merge the two lists in - * classpath order. To keep it simple, the lists are simply concatenated, - * with the same syntax priorities as - * {@link #parseFileAnySyntax(File,ConfigParseOptions) parseFileAnySyntax()} - * - all ".conf" resources are ahead of all ".json" resources which are - * ahead of all ".properties" resources. - * - * @param klass - * class which determines the ClassLoader and the - * package for relative resource names - * @param resourceBasename - * a resource name as in {@link java.lang.Class#getResource}, - * with or without extension - * @param options - * parse options (class loader is ignored in favor of the one - * from klass) - * @return the parsed configuration - */ - public static Config parseResourcesAnySyntax(Class klass, String resourceBasename, - ConfigParseOptions options) { - return ConfigImpl.parseResourcesAnySyntax(klass, resourceBasename, - options).toConfig(); - } - - /** - * Like {@link #parseResourcesAnySyntax(Class,String,ConfigParseOptions)} - * but always uses default parse options. - * - * @param klass - * klass.getClassLoader() will be used to load - * resources, and non-absolute resource names will have this - * class's package added - * @param resourceBasename - * a resource name as in {@link java.lang.Class#getResource}, - * with or without extension - * @return the parsed configuration - */ - public static Config parseResourcesAnySyntax(Class klass, String resourceBasename) { - return parseResourcesAnySyntax(klass, resourceBasename, ConfigParseOptions.defaults()); - } - - /** - * Parses all resources on the classpath with the given name and merges them - * into a single Config. - * - *

- * This works like {@link java.lang.ClassLoader#getResource}, not like - * {@link java.lang.Class#getResource}, so the name never begins with a - * slash. - * - *

- * See {@link #parseResources(Class,String,ConfigParseOptions)} for full - * details. - * - * @param loader - * will be used to load resources by setting this loader on the - * provided options - * @param resource - * resource to look up - * @param options - * parse options (class loader is ignored) - * @return the parsed configuration - */ - public static Config parseResources(ClassLoader loader, String resource, - ConfigParseOptions options) { - return parseResources(resource, options.setClassLoader(loader)); - } - - /** - * Like {@link #parseResources(ClassLoader,String,ConfigParseOptions)} but always uses - * default parse options. - * - * @param loader - * will be used to load resources - * @param resource - * resource to look up in the loader - * @return the parsed configuration - */ - public static Config parseResources(ClassLoader loader, String resource) { - return parseResources(loader, resource, ConfigParseOptions.defaults()); - } - - /** - * Parses classpath resources with a flexible extension. In general, this - * method has the same behavior as - * {@link #parseFileAnySyntax(File,ConfigParseOptions)} but for classpath - * resources instead, as in - * {@link #parseResources(ClassLoader,String,ConfigParseOptions)}. - * - *

- * {@link #parseResourcesAnySyntax(Class,String,ConfigParseOptions)} differs - * in the syntax for the resource name, but otherwise see - * {@link #parseResourcesAnySyntax(Class,String,ConfigParseOptions)} for - * some details and caveats on this method. - * - * @param loader - * class loader to look up resources in, will be set on options - * @param resourceBasename - * a resource name as in - * {@link java.lang.ClassLoader#getResource}, with or without - * extension - * @param options - * parse options (class loader ignored) - * @return the parsed configuration - */ - public static Config parseResourcesAnySyntax(ClassLoader loader, String resourceBasename, - ConfigParseOptions options) { - return ConfigImpl.parseResourcesAnySyntax(resourceBasename, options.setClassLoader(loader)) - .toConfig(); - } - - /** - * Like {@link #parseResourcesAnySyntax(ClassLoader,String,ConfigParseOptions)} but always uses - * default parse options. - * - * @param loader - * will be used to load resources - * @param resourceBasename - * a resource name as in - * {@link java.lang.ClassLoader#getResource}, with or without - * extension - * @return the parsed configuration - */ - public static Config parseResourcesAnySyntax(ClassLoader loader, String resourceBasename) { - return parseResourcesAnySyntax(loader, resourceBasename, ConfigParseOptions.defaults()); - } - - /** - * Like {@link #parseResources(ClassLoader,String,ConfigParseOptions)} but - * uses thread's current context class loader if none is set in the - * ConfigParseOptions. - * @param resource the resource name - * @param options parse options - * @return the parsed configuration - */ - public static Config parseResources(String resource, ConfigParseOptions options) { - ConfigParseOptions withLoader = ensureClassLoader(options, "parseResources"); - return Parseable.newResources(resource, withLoader).parse().toConfig(); - } - - /** - * Like {@link #parseResources(ClassLoader,String)} but uses thread's - * current context class loader. - * @param resource the resource name - * @return the parsed configuration - */ - public static Config parseResources(String resource) { - return parseResources(resource, ConfigParseOptions.defaults()); - } - - /** - * Like - * {@link #parseResourcesAnySyntax(ClassLoader,String,ConfigParseOptions)} - * but uses thread's current context class loader. - * @param resourceBasename the resource basename (no file type suffix) - * @param options parse options - * @return the parsed configuration - */ - public static Config parseResourcesAnySyntax(String resourceBasename, ConfigParseOptions options) { - return ConfigImpl.parseResourcesAnySyntax(resourceBasename, options).toConfig(); - } - - /** - * Like {@link #parseResourcesAnySyntax(ClassLoader,String)} but uses - * thread's current context class loader. - * @param resourceBasename the resource basename (no file type suffix) - * @return the parsed configuration - */ - public static Config parseResourcesAnySyntax(String resourceBasename) { - return parseResourcesAnySyntax(resourceBasename, ConfigParseOptions.defaults()); - } - - /** - * Parse only any application replacement (specified by one of config.{resource,file,url}), returning - * an empty Config if no overrides were set. - * - * @since 1.4.1 - * - * @return a {@link java.util.Optional} containing any specified replacement, or {@link Optional#empty()} - * if none was specified. - */ - public static java.util.Optional parseApplicationReplacement() { - return parseApplicationReplacement(ConfigParseOptions.defaults()); - } - - /** - * Like {@link #parseApplicationReplacement()} but allows you to specify a class loader - * ti yse rather than the current context class loader. - * - * @since 1.4.1 - * - * @param loader the class loader - * @return a {@link java.util.Optional} containing any specified replacement, or {@link Optional#empty()} - * if none was specified. - */ - public static java.util.Optional parseApplicationReplacement(ClassLoader loader) { - return parseApplicationReplacement(ConfigParseOptions.defaults().setClassLoader(loader)); - } - - /** - * Like {@link #parseApplicationReplacement()} but allows you to specify parse options. - * - * @since 1.4.1 - * - * @param parseOptions parse options - * @return a {@link java.util.Optional} containing any specified replacement, or {@link Optional#empty()} - * if none was specified. - */ - public static java.util.Optional parseApplicationReplacement(ConfigParseOptions parseOptions) { - final ConfigParseOptions withLoader = ensureClassLoader(parseOptions, "parseApplicationReplacement"); - ClassLoader loader = withLoader.getClassLoader(); - - int specified = 0; - - // override application.conf with config.file, config.resource, - // config.url if requested. - String resource = System.getProperty("config.resource"); - if (resource != null) - specified += 1; - String file = System.getProperty("config.file"); - if (file != null) - specified += 1; - String url = System.getProperty("config.url"); - if (url != null) - specified += 1; - - if (specified == 0) { - return java.util.Optional.empty(); - } else if (specified > 1) { - throw new ConfigException.Generic("You set more than one of config.file='" + file - + "', config.url='" + url + "', config.resource='" + resource - + "'; don't know which one to use!"); - } else { - // the override file/url/resource MUST be present or it's an error - ConfigParseOptions overrideOptions = parseOptions.setAllowMissing(false); - if (resource != null) { - if (resource.startsWith("/")) - resource = resource.substring(1); - // this deliberately does not parseResourcesAnySyntax; if - // people want that they can use an include statement. - return java.util.Optional.of(ConfigFactory.parseResources(loader, resource, overrideOptions)); - } else if (file != null) { - return java.util.Optional.of(ConfigFactory.parseFile(new File(file), overrideOptions)); - } else { - try { - return java.util.Optional.of(ConfigFactory.parseURL(new URL(url), overrideOptions)); - } catch (MalformedURLException e) { - throw new ConfigException.Generic("Bad URL in config.url system property: '" - + url + "': " + e.getMessage(), e); - } - } - } - - } - - /** - * Parses a string (which should be valid HOCON or JSON by default, or - * the syntax specified in the options otherwise). - * - * @param s string to parse - * @param options parse options - * @return the parsed configuration - */ - public static Config parseString(String s, ConfigParseOptions options) { - return Parseable.newString(s, options).parse().toConfig(); - } - - /** - * Parses a string (which should be valid HOCON or JSON). - * - * @param s string to parse - * @return the parsed configuration - */ - public static Config parseString(String s) { - return parseString(s, ConfigParseOptions.defaults()); - } - - /** - * Creates a {@code Config} based on a {@link java.util.Map} from paths to - * plain Java values. Similar to - * {@link ConfigValueFactory#fromMap(Map,String)}, except the keys in the - * map are path expressions, rather than keys; and correspondingly it - * returns a {@code Config} instead of a {@code ConfigObject}. This is more - * convenient if you are writing literal maps in code, and less convenient - * if you are getting your maps from some data source such as a parser. - * - *

- * An exception will be thrown (and it is a bug in the caller of the method) - * if a path is both an object and a value, for example if you had both - * "a=foo" and "a.b=bar", then "a" is both the string "foo" and the parent - * object of "b". The caller of this method should ensure that doesn't - * happen. - * - * @param values map from paths to plain Java objects - * @param originDescription - * description of what this map represents, like a filename, or - * "default settings" (origin description is used in error - * messages) - * @return the map converted to a {@code Config} - */ - public static Config parseMap(Map values, - String originDescription) { - return ConfigImpl.fromPathMap(values, originDescription).toConfig(); - } - - /** - * See the other overload of {@link #parseMap(Map, String)} for details, - * this one just uses a default origin description. - * - * @param values map from paths to plain Java values - * @return the map converted to a {@code Config} - */ - public static Config parseMap(Map values) { - return parseMap(values, null); - } - - private static com.gu.typesafe.config.ConfigLoadingStrategy getConfigLoadingStrategy() { - String className = System.getProperties().getProperty(STRATEGY_PROPERTY_NAME); - - if (className != null) { - try { - return ConfigLoadingStrategy.class.cast(Class.forName(className).newInstance()); - } catch (Throwable e) { - throw new ConfigException.BugOrBroken("Failed to load strategy: " + className, e); - } - } else { - return new DefaultConfigLoadingStrategy(); - } - } - - private static Boolean getOverrideWithEnv() { - String overrideWithEnv = System.getProperties().getProperty(OVERRIDE_WITH_ENV_PROPERTY_NAME); - - return Boolean.parseBoolean(overrideWithEnv); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigIncludeContext.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigIncludeContext.java deleted file mode 100644 index cd09e709c1..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigIncludeContext.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - - -/** - * Context provided to a {@link ConfigIncluder}; this interface is only useful - * inside a {@code ConfigIncluder} implementation, and is not intended for apps - * to implement. - * - *

- * Do not implement this interface; it should only be implemented by - * the config library. Arbitrary implementations will not work because the - * library internals assume a specific concrete implementation. Also, this - * interface is likely to grow new methods over time, so third-party - * implementations will break. - */ -public interface ConfigIncludeContext { - /** - * Tries to find a name relative to whatever is doing the including, for - * example in the same directory as the file doing the including. Returns - * null if it can't meaningfully create a relative name. The returned - * parseable may not exist; this function is not required to do any IO, just - * compute what the name would be. - * - * The passed-in filename has to be a complete name (with extension), not - * just a basename. (Include statements in config files are allowed to give - * just a basename.) - * - * @param filename - * the name to make relative to the resource doing the including - * @return parseable item relative to the resource doing the including, or - * null - */ - com.gu.typesafe.config.ConfigParseable relativeTo(String filename); - - /** - * Parse options to use (if you use another method to get a - * {@link com.gu.typesafe.config.ConfigParseable} then use {@link ConfigParseable#options()} - * instead though). - * - * @return the parse options - */ - com.gu.typesafe.config.ConfigParseOptions parseOptions(); - - - /** - * Copy this {@link ConfigIncludeContext} giving it a new value for its parseOptions. - * - * @param options new parse options to use - * - * @return the updated copy of this context - */ - ConfigIncludeContext setParseOptions(ConfigParseOptions options); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluder.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluder.java deleted file mode 100644 index 5882748693..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluder.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -/** - * Implement this interface and provide an instance to - * {@link ConfigParseOptions#setIncluder ConfigParseOptions.setIncluder()} to - * customize handling of {@code include} statements in config files. You may - * also want to implement {@link com.gu.typesafe.config.ConfigIncluderClasspath}, - * {@link com.gu.typesafe.config.ConfigIncluderFile}, and {@link ConfigIncluderURL}, or not. - */ -public interface ConfigIncluder { - /** - * Returns a new includer that falls back to the given includer. This is how - * you can obtain the default includer; it will be provided as a fallback. - * It's up to your includer to chain to it if you want to. You might want to - * merge any files found by the fallback includer with any objects you load - * yourself. - * - * It's important to handle the case where you already have the fallback - * with a "return this", i.e. this method should not create a new object if - * the fallback is the same one you already have. The same fallback may be - * added repeatedly. - * - * @param fallback the previous includer for chaining - * @return a new includer - */ - ConfigIncluder withFallback(ConfigIncluder fallback); - - /** - * Parses another item to be included. The returned object typically would - * not have substitutions resolved. You can throw a ConfigException here to - * abort parsing, or return an empty object, but may not return null. - * - * This method is used for a "heuristic" include statement that does not - * specify file, URL, or classpath resource. If the include statement does - * specify, then the same class implementing {@link ConfigIncluder} must - * also implement {@link ConfigIncluderClasspath}, - * {@link ConfigIncluderFile}, or {@link ConfigIncluderURL} as needed, or a - * default includer will be used. - * - * @param context - * some info about the include context - * @param what - * the include statement's argument - * @return a non-null ConfigObject - */ - ConfigObject include(ConfigIncludeContext context, String what); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluderClasspath.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluderClasspath.java deleted file mode 100644 index 44a9678766..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluderClasspath.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -/** - * Implement this in addition to {@link ConfigIncluder} if you want to - * support inclusion of files with the {@code include classpath("resource")} - * syntax. If you do not implement this but do implement {@link ConfigIncluder}, - * attempts to load classpath resources will use the default includer. - */ -public interface ConfigIncluderClasspath { - /** - * Parses another item to be included. The returned object typically would - * not have substitutions resolved. You can throw a ConfigException here to - * abort parsing, or return an empty object, but may not return null. - * - * @param context - * some info about the include context - * @param what - * the include statement's argument - * @return a non-null ConfigObject - */ - ConfigObject includeResources(ConfigIncludeContext context, String what); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluderFile.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluderFile.java deleted file mode 100644 index 31536ed64a..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluderFile.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -import java.io.File; - -/** - * Implement this in addition to {@link ConfigIncluder} if you want to - * support inclusion of files with the {@code include file("filename")} syntax. - * If you do not implement this but do implement {@link ConfigIncluder}, - * attempts to load files will use the default includer. - */ -public interface ConfigIncluderFile { - /** - * Parses another item to be included. The returned object typically would - * not have substitutions resolved. You can throw a ConfigException here to - * abort parsing, or return an empty object, but may not return null. - * - * @param context - * some info about the include context - * @param what - * the include statement's argument - * @return a non-null ConfigObject - */ - ConfigObject includeFile(ConfigIncludeContext context, File what); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluderURL.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluderURL.java deleted file mode 100644 index 29850f0487..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigIncluderURL.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -import java.net.URL; - -/** - * Implement this in addition to {@link com.gu.typesafe.config.ConfigIncluder} if you want to - * support inclusion of files with the {@code include url("http://example.com")} - * syntax. If you do not implement this but do implement {@link ConfigIncluder}, - * attempts to load URLs will use the default includer. - */ -public interface ConfigIncluderURL { - /** - * Parses another item to be included. The returned object typically would - * not have substitutions resolved. You can throw a ConfigException here to - * abort parsing, or return an empty object, but may not return null. - * - * @param context - * some info about the include context - * @param what - * the include statement's argument - * @return a non-null ConfigObject - */ - ConfigObject includeURL(ConfigIncludeContext context, URL what); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigList.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigList.java deleted file mode 100644 index 1214efe9ca..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigList.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -import java.util.List; - -/** - * Subtype of {@link ConfigValue} representing a list value, as in JSON's - * {@code [1,2,3]} syntax. - * - *

- * {@code ConfigList} implements {@code java.util.List} so you can - * use it like a regular Java list. Or call {@link #unwrapped()} to unwrap the - * list elements into plain Java values. - * - *

- * Like all {@link ConfigValue} subtypes, {@code ConfigList} is immutable. This - * makes it threadsafe and you never have to create "defensive copies." The - * mutator methods from {@link java.util.List} all throw - * {@link java.lang.UnsupportedOperationException}. - * - *

- * The {@link ConfigValue#valueType} method on a list returns - * {@link ConfigValueType#LIST}. - * - *

- * Do not implement {@code ConfigList}; it should only be implemented - * by the config library. Arbitrary implementations will not work because the - * library internals assume a specific concrete implementation. Also, this - * interface is likely to grow new methods over time, so third-party - * implementations will break. - * - */ -public interface ConfigList extends List, ConfigValue { - - /** - * Recursively unwraps the list, returning a list of plain Java values such - * as Integer or String or whatever is in the list. - * - * @return a {@link java.util.List} containing plain Java objects - */ - @Override - List unwrapped(); - - @Override - ConfigList withOrigin(ConfigOrigin origin); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigLoadingStrategy.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigLoadingStrategy.java deleted file mode 100644 index 3476624914..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigLoadingStrategy.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.gu.typesafe.config; - -/** - * This method allows you to alter default config loading strategy for all the code which - * calls {@link ConfigFactory#load}. - * - * Usually you don't have to implement this interface but it may be required - * when you fixing a improperly implemented library with unavailable source code. - * - * You have to define VM property {@code config.strategy} to replace default strategy with your own. - */ -public interface ConfigLoadingStrategy { - /** - * This method must load and parse application config. - * - * @param parseOptions {@link ConfigParseOptions} to use - * @return loaded config - */ - Config parseApplicationConfig(ConfigParseOptions parseOptions); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigMemorySize.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigMemorySize.java deleted file mode 100644 index a32afd32d0..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigMemorySize.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config; - -import java.math.BigInteger; - -/** - * An immutable class representing an amount of memory. Use - * static factory methods such as {@link - * ConfigMemorySize#ofBytes(BigInteger)} to create instances. - * - * @since 1.3.0 - */ -public final class ConfigMemorySize { - - private BigInteger bytes; - - private ConfigMemorySize(BigInteger bytes) { - if (bytes.signum() < 0) - throw new IllegalArgumentException("Attempt to construct ConfigMemorySize with negative number: " + bytes); - this.bytes = bytes; - } - - /** - * Constructs a ConfigMemorySize representing the given - * number of bytes. - * @since 1.3.0 - * @param bytes a number of bytes - * @return an instance representing the number of bytes - */ - public static ConfigMemorySize ofBytes(BigInteger bytes) { - return new ConfigMemorySize(bytes); - } - - /** - * Constructs a ConfigMemorySize representing the given - * number of bytes. - * @param bytes a number of bytes - * @return an instance representing the number of bytes - */ - public static ConfigMemorySize ofBytes(long bytes) { - return new ConfigMemorySize(BigInteger.valueOf(bytes)); - } - - /** - * Gets the size in bytes. - * - * @since 1.3.0 - * @return how many bytes - * @exception IllegalArgumentException when memory value - * in bytes doesn't fit in a long value. Consider using - * {@link #toBytesBigInteger} in this case. - */ - public long toBytes() { - if (bytes.bitLength() < 64) - return bytes.longValue(); - else - throw new IllegalArgumentException( - "size-in-bytes value is out of range for a 64-bit long: '" + bytes + "'"); - } - - /** - * Gets the size in bytes. The behavior of this method - * is the same as that of the {@link #toBytes()} method, - * except that the number of bytes returned as a - * BigInteger value. Use it when memory value in bytes - * doesn't fit in a long value. - * @return how many bytes - */ - public BigInteger toBytesBigInteger() { - return bytes; - } - - @Override - public String toString() { - return "ConfigMemorySize(" + bytes + ")"; - } - - @Override - public boolean equals(Object other) { - if (other instanceof ConfigMemorySize) { - return ((ConfigMemorySize)other).bytes.equals(this.bytes); - } else { - return false; - } - } - - @Override - public int hashCode() { - // in Java 8 this can become Long.hashCode(bytes) - return bytes.hashCode(); - } - -} - diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigMergeable.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigMergeable.java deleted file mode 100644 index 264b3c0092..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigMergeable.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -/** - * Marker for types whose instances can be merged, that is {@link Config} and - * {@link ConfigValue}. Instances of {@code Config} and {@code ConfigValue} can - * be combined into a single new instance using the - * {@link ConfigMergeable#withFallback withFallback()} method. - * - *

- * Do not implement this interface; it should only be implemented by - * the config library. Arbitrary implementations will not work because the - * library internals assume a specific concrete implementation. Also, this - * interface is likely to grow new methods over time, so third-party - * implementations will break. - */ -public interface ConfigMergeable { - /** - * Returns a new value computed by merging this value with another, with - * keys in this value "winning" over the other one. - * - *

- * This associative operation may be used to combine configurations from - * multiple sources (such as multiple configuration files). - * - *

- * The semantics of merging are described in the spec - * for HOCON. Merging typically occurs when either the same object is - * created twice in the same file, or two config files are both loaded. For - * example: - * - *

-     *  foo = { a: 42 }
-     *  foo = { b: 43 }
-     * 
- * - * Here, the two objects are merged as if you had written: - * - *
-     *  foo = { a: 42, b: 43 }
-     * 
- * - *

- * Only {@link ConfigObject} and {@link Config} instances do anything in - * this method (they need to merge the fallback keys into themselves). All - * other values just return the original value, since they automatically - * override any fallback. This means that objects do not merge "across" - * non-objects; if you write - * object.withFallback(nonObject).withFallback(otherObject), - * then otherObject will simply be ignored. This is an - * intentional part of how merging works, because non-objects such as - * strings and integers replace (rather than merging with) any prior value: - * - *

-     * foo = { a: 42 }
-     * foo = 10
-     * 
- * - * Here, the number 10 "wins" and the value of foo would be - * simply 10. Again, for details see the spec. - * - * @param other - * an object whose keys should be used as fallbacks, if the keys - * are not present in this one - * @return a new object (or the original one, if the fallback doesn't get - * used) - */ - ConfigMergeable withFallback(ConfigMergeable other); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigObject.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigObject.java deleted file mode 100644 index 3c1120214b..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigObject.java +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -import java.util.Map; - -/** - * Subtype of {@link com.gu.typesafe.config.ConfigValue} representing an object (AKA dictionary or map) - * value, as in JSON's curly brace { "a" : 42 } syntax. - * - *

- * An object may also be viewed as a {@link Config} by calling - * {@link ConfigObject#toConfig()}. - * - *

- * {@code ConfigObject} implements {@code java.util.Map} so - * you can use it like a regular Java map. Or call {@link #unwrapped()} to - * unwrap the map to a map with plain Java values rather than - * {@code ConfigValue}. - * - *

- * Like all {@link com.gu.typesafe.config.ConfigValue} subtypes, {@code ConfigObject} is immutable. - * This makes it threadsafe and you never have to create "defensive copies." The - * mutator methods from {@link java.util.Map} all throw - * {@link java.lang.UnsupportedOperationException}. - * - *

- * The {@link com.gu.typesafe.config.ConfigValue#valueType} method on an object returns - * {@link ConfigValueType#OBJECT}. - * - *

- * In most cases you want to use the {@link Config} interface rather than this - * one. Call {@link #toConfig()} to convert a {@code ConfigObject} to a - * {@code Config}. - * - *

- * The API for a {@code ConfigObject} is in terms of keys, while the API for a - * {@link Config} is in terms of path expressions. Conceptually, - * {@code ConfigObject} is a tree of maps from keys to values, while a - * {@code Config} is a one-level map from paths to values. - * - *

- * Use {@link com.gu.typesafe.config.ConfigUtil#joinPath} and {@link ConfigUtil#splitPath} to convert - * between path expressions and individual path elements (keys). - * - *

- * A {@code ConfigObject} may contain null values, which will have - * {@link com.gu.typesafe.config.ConfigValue#valueType()} equal to {@link ConfigValueType#NULL}. If - * {@link ConfigObject#get(Object)} returns Java's null then the key was not - * present in the parsed file (or wherever this value tree came from). If - * {@code get("key")} returns a {@link com.gu.typesafe.config.ConfigValue} with type - * {@code ConfigValueType#NULL} then the key was set to null explicitly in the - * config file. - * - *

- * Do not implement interface {@code ConfigObject}; it should only be - * implemented by the config library. Arbitrary implementations will not work - * because the library internals assume a specific concrete implementation. - * Also, this interface is likely to grow new methods over time, so third-party - * implementations will break. - */ -public interface ConfigObject extends com.gu.typesafe.config.ConfigValue, Map { - - /** - * Converts this object to a {@link Config} instance, enabling you to use - * path expressions to find values in the object. This is a constant-time - * operation (it is not proportional to the size of the object). - * - * @return a {@link Config} with this object as its root - */ - Config toConfig(); - - /** - * Recursively unwraps the object, returning a map from String to whatever - * plain Java values are unwrapped from the object's values. - * - * @return a {@link java.util.Map} containing plain Java objects - */ - @Override - Map unwrapped(); - - @Override - ConfigObject withFallback(ConfigMergeable other); - - /** - * Gets a {@link com.gu.typesafe.config.ConfigValue} at the given key, or returns null if there is - * no value. The returned {@link com.gu.typesafe.config.ConfigValue} may have - * {@link ConfigValueType#NULL} or any other type, and the passed-in key - * must be a key in this object (rather than a path expression). - * - * @param key - * key to look up - * - * @return the value at the key or null if none - */ - @Override - com.gu.typesafe.config.ConfigValue get(Object key); - - /** - * Clone the object with only the given key (and its children) retained; all - * sibling keys are removed. - * - * @param key - * key to keep - * @return a copy of the object minus all keys except the one specified - */ - ConfigObject withOnlyKey(String key); - - /** - * Clone the object with the given key removed. - * - * @param key - * key to remove - * @return a copy of the object minus the specified key - */ - ConfigObject withoutKey(String key); - - /** - * Returns a {@code ConfigObject} based on this one, but with the given key - * set to the given value. Does not modify this instance (since it's - * immutable). If the key already has a value, that value is replaced. To - * remove a value, use {@link ConfigObject#withoutKey(String)}. - * - * @param key - * key to add - * @param value - * value at the new key - * @return the new instance with the new map entry - */ - ConfigObject withValue(String key, ConfigValue value); - - @Override - ConfigObject withOrigin(ConfigOrigin origin); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigOrigin.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigOrigin.java deleted file mode 100644 index 3d81ad3bf4..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigOrigin.java +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -import java.net.URL; -import java.util.List; - - -/** - * Represents the origin (such as filename and line number) of a - * {@link com.gu.typesafe.config.ConfigValue} for use in error messages. Obtain the origin of a value - * with {@link com.gu.typesafe.config.ConfigValue#origin}. Exceptions may have an origin, see - * {@link ConfigException#origin}, but be careful because - * ConfigException.origin() may return null. - * - *

- * It's best to use this interface only for debugging; its accuracy is - * "best effort" rather than guaranteed, and a potentially-noticeable amount of - * memory could probably be saved if origins were not kept around, so in the - * future there might be some option to discard origins. - * - *

- * Do not implement this interface; it should only be implemented by - * the config library. Arbitrary implementations will not work because the - * library internals assume a specific concrete implementation. Also, this - * interface is likely to grow new methods over time, so third-party - * implementations will break. - */ -public interface ConfigOrigin { - /** - * Returns a string describing the origin of a value or exception. This will - * never return null. - * - * @return string describing the origin - */ - public String description(); - - /** - * Returns a filename describing the origin. This will return null if the - * origin was not a file. - * - * @return filename of the origin or null - */ - public String filename(); - - /** - * Returns a URL describing the origin. This will return null if the origin - * has no meaningful URL. - * - * @return url of the origin or null - */ - public URL url(); - - /** - * Returns a classpath resource name describing the origin. This will return - * null if the origin was not a classpath resource. - * - * @return resource name of the origin or null - */ - public String resource(); - - /** - * Returns a line number where the value or exception originated. This will - * return -1 if there's no meaningful line number. - * - * @return line number or -1 if none is available - */ - public int lineNumber(); - - /** - * Returns any comments that appeared to "go with" this place in the file. - * Often an empty list, but never null. The details of this are subject to - * change, but at the moment comments that are immediately before an array - * element or object field, with no blank line after the comment, "go with" - * that element or field. - * - * @return any comments that seemed to "go with" this origin, empty list if - * none - */ - public List comments(); - - /** - * Returns a {@code ConfigOrigin} based on this one, but with the given - * comments. Does not modify this instance or any {@code ConfigValue}s with - * this origin (since they are immutable). To set the returned origin to a - * {@code ConfigValue}, use {@link com.gu.typesafe.config.ConfigValue#withOrigin}. - * - *

- * Note that when the given comments are equal to the comments on this object, - * a new instance may not be created and {@code this} is returned directly. - * - * @since 1.3.0 - * - * @param comments the comments used on the returned origin - * @return the ConfigOrigin with the given comments - */ - public ConfigOrigin withComments(List comments); - - /** - * Returns a {@code ConfigOrigin} based on this one, but with the given - * line number. This origin must be a FILE, URL or RESOURCE. Does not modify - * this instance or any {@code ConfigValue}s with this origin (since they are - * immutable). To set the returned origin to a {@code ConfigValue}, use - * {@link ConfigValue#withOrigin}. - * - *

- * Note that when the given lineNumber are equal to the lineNumber on this - * object, a new instance may not be created and {@code this} is returned - * directly. - * - * @since 1.3.0 - * - * @param lineNumber the new line number - * @return the created ConfigOrigin - */ - public ConfigOrigin withLineNumber(int lineNumber); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigOriginFactory.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigOriginFactory.java deleted file mode 100644 index 223856bedc..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigOriginFactory.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.gu.typesafe.config; - -import java.net.URL; - -import com.gu.typesafe.config.impl.ConfigImpl; - -/** - * This class contains some static factory methods for building a {@link - * com.gu.typesafe.config.ConfigOrigin}. {@code ConfigOrigin}s are automatically created when you - * call other API methods to get a {@code ConfigValue} or {@code Config}. - * But you can also set the origin of an existing {@code ConfigValue}, using - * {@link ConfigValue#withOrigin(com.gu.typesafe.config.ConfigOrigin)}. - * - * @since 1.3.0 - */ -public final class ConfigOriginFactory { - private ConfigOriginFactory() { - } - - /** - * Returns the default origin for values when no other information is - * provided. This is the origin used in {@link ConfigValueFactory - * #fromAnyRef(Object)}. - * - * @since 1.3.0 - * - * @return the default origin - */ - public static com.gu.typesafe.config.ConfigOrigin newSimple() { - return newSimple(null); - } - - /** - * Returns an origin with the given description. - * - * @since 1.3.0 - * - * @param description brief description of what the origin is - * @return a new origin - */ - public static com.gu.typesafe.config.ConfigOrigin newSimple(String description) { - return ConfigImpl.newSimpleOrigin(description); - } - - /** - * Creates a file origin with the given filename. - * - * @since 1.3.0 - * - * @param filename the filename of this origin - * @return a new origin - */ - public static com.gu.typesafe.config.ConfigOrigin newFile(String filename) { - return ConfigImpl.newFileOrigin(filename); - } - - /** - * Creates a url origin with the given URL object. - * - * @since 1.3.0 - * - * @param url the url of this origin - * @return a new origin - */ - public static ConfigOrigin newURL(URL url) { - return ConfigImpl.newURLOrigin(url); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigParseOptions.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigParseOptions.java deleted file mode 100644 index ed5097938e..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigParseOptions.java +++ /dev/null @@ -1,242 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - - -import com.gu.typesafe.config.impl.ConfigImplUtil; - -/** - * A set of options related to parsing. - * - *

- * This object is immutable, so the "setters" return a new object. - * - *

- * Here is an example of creating a custom {@code ConfigParseOptions}: - * - *

- *     ConfigParseOptions options = ConfigParseOptions.defaults()
- *         .setSyntax(ConfigSyntax.JSON)
- *         .setAllowMissing(false)
- * 
- * - */ -public final class ConfigParseOptions { - final com.gu.typesafe.config.ConfigSyntax syntax; - final String originDescription; - final boolean allowMissing; - final com.gu.typesafe.config.ConfigIncluder includer; - final ClassLoader classLoader; - - private ConfigParseOptions(com.gu.typesafe.config.ConfigSyntax syntax, String originDescription, boolean allowMissing, - com.gu.typesafe.config.ConfigIncluder includer, ClassLoader classLoader) { - this.syntax = syntax; - this.originDescription = originDescription; - this.allowMissing = allowMissing; - this.includer = includer; - this.classLoader = classLoader; - } - - /** - * Gets an instance of ConfigParseOptions with all fields - * set to the default values. Start with this instance and make any - * changes you need. - * @return the default parse options - */ - public static ConfigParseOptions defaults() { - return new ConfigParseOptions(null, null, true, null, null); - } - - /** - * Set the file format. If set to null, try to guess from any available - * filename extension; if guessing fails, assume {@link com.gu.typesafe.config.ConfigSyntax#CONF}. - * - * @param syntax - * a syntax or {@code null} for best guess - * @return options with the syntax set - */ - public ConfigParseOptions setSyntax(com.gu.typesafe.config.ConfigSyntax syntax) { - if (this.syntax == syntax) - return this; - else - return new ConfigParseOptions(syntax, this.originDescription, this.allowMissing, - this.includer, this.classLoader); - } - - /** - * Set the file format. If set to null, assume {@link com.gu.typesafe.config.ConfigSyntax#CONF}. - * - * @param filename - * a configuration file name - * @return options with the syntax set - */ - public ConfigParseOptions setSyntaxFromFilename(String filename) { - com.gu.typesafe.config.ConfigSyntax syntax = ConfigImplUtil.syntaxFromExtension(filename); - return setSyntax(syntax); - } - - /** - * Gets the current syntax option, which may be null for "any". - * @return the current syntax or null - */ - public ConfigSyntax getSyntax() { - return syntax; - } - - /** - * Set a description for the thing being parsed. In most cases this will be - * set up for you to something like the filename, but if you provide just an - * input stream you might want to improve on it. Set to null to allow the - * library to come up with something automatically. This description is the - * basis for the {@link ConfigOrigin} of the parsed values. - * - * @param originDescription description to put in the {@link ConfigOrigin} - * @return options with the origin description set - */ - public ConfigParseOptions setOriginDescription(String originDescription) { - // findbugs complains about == here but is wrong, do not "fix" - if (this.originDescription == originDescription) - return this; - else if (this.originDescription != null && originDescription != null - && this.originDescription.equals(originDescription)) - return this; - else - return new ConfigParseOptions(this.syntax, originDescription, this.allowMissing, - this.includer, this.classLoader); - } - - /** - * Gets the current origin description, which may be null for "automatic". - * @return the current origin description or null - */ - public String getOriginDescription() { - return originDescription; - } - - /** this is package-private, not public API */ - ConfigParseOptions withFallbackOriginDescription(String originDescription) { - if (this.originDescription == null) - return setOriginDescription(originDescription); - else - return this; - } - - /** - * Set to false to throw an exception if the item being parsed (for example - * a file) is missing. Set to true to just return an empty document in that - * case. Note that this setting applies on only to fetching the root document, - * it has no effect on any nested includes. - * - * @param allowMissing true to silently ignore missing item - * @return options with the "allow missing" flag set - */ - public ConfigParseOptions setAllowMissing(boolean allowMissing) { - if (this.allowMissing == allowMissing) - return this; - else - return new ConfigParseOptions(this.syntax, this.originDescription, allowMissing, - this.includer, this.classLoader); - } - - /** - * Gets the current "allow missing" flag. - * @return whether we allow missing files - */ - public boolean getAllowMissing() { - return allowMissing; - } - - /** - * Set a {@link com.gu.typesafe.config.ConfigIncluder} which customizes how includes are handled. - * null means to use the default includer. - * - * @param includer the includer to use or null for default - * @return new version of the parse options with different includer - */ - public ConfigParseOptions setIncluder(com.gu.typesafe.config.ConfigIncluder includer) { - if (this.includer == includer) - return this; - else - return new ConfigParseOptions(this.syntax, this.originDescription, this.allowMissing, - includer, this.classLoader); - } - - /** - * Prepends a {@link com.gu.typesafe.config.ConfigIncluder} which customizes how - * includes are handled. To prepend your includer, the - * library calls {@link com.gu.typesafe.config.ConfigIncluder#withFallback} on your - * includer to append the existing includer to it. - * - * @param includer the includer to prepend (may not be null) - * @return new version of the parse options with different includer - */ - public ConfigParseOptions prependIncluder(com.gu.typesafe.config.ConfigIncluder includer) { - if (includer == null) - throw new NullPointerException("null includer passed to prependIncluder"); - if (this.includer == includer) - return this; - else if (this.includer != null) - return setIncluder(includer.withFallback(this.includer)); - else - return setIncluder(includer); - } - - /** - * Appends a {@link com.gu.typesafe.config.ConfigIncluder} which customizes how - * includes are handled. To append, the library calls {@link - * com.gu.typesafe.config.ConfigIncluder#withFallback} on the existing includer. - * - * @param includer the includer to append (may not be null) - * @return new version of the parse options with different includer - */ - public ConfigParseOptions appendIncluder(com.gu.typesafe.config.ConfigIncluder includer) { - if (includer == null) - throw new NullPointerException("null includer passed to appendIncluder"); - if (this.includer == includer) - return this; - else if (this.includer != null) - return setIncluder(this.includer.withFallback(includer)); - else - return setIncluder(includer); - } - - /** - * Gets the current includer (will be null for the default includer). - * @return current includer or null - */ - public ConfigIncluder getIncluder() { - return includer; - } - - /** - * Set the class loader. If set to null, - * Thread.currentThread().getContextClassLoader() will be used. - * - * @param loader - * a class loader or {@code null} to use thread context class - * loader - * @return options with the class loader set - */ - public ConfigParseOptions setClassLoader(ClassLoader loader) { - if (this.classLoader == loader) - return this; - else - return new ConfigParseOptions(this.syntax, this.originDescription, this.allowMissing, - this.includer, loader); - } - - /** - * Get the class loader; never returns {@code null}, if the class loader was - * unset, returns - * Thread.currentThread().getContextClassLoader(). - * - * @return class loader to use - */ - public ClassLoader getClassLoader() { - if (this.classLoader == null) - return Thread.currentThread().getContextClassLoader(); - else - return this.classLoader; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigParseable.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigParseable.java deleted file mode 100644 index 777b1034b8..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigParseable.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - - -/** - * An opaque handle to something that can be parsed, obtained from - * {@link ConfigIncludeContext}. - * - *

- * Do not implement this interface; it should only be implemented by - * the config library. Arbitrary implementations will not work because the - * library internals assume a specific concrete implementation. Also, this - * interface is likely to grow new methods over time, so third-party - * implementations will break. - */ -public interface ConfigParseable { - /** - * Parse whatever it is. The options should come from - * {@link ConfigParseable#options options()} but you could tweak them if you - * like. - * - * @param options - * parse options, should be based on the ones from - * {@link ConfigParseable#options options()} - * @return the parsed object - */ - ConfigObject parse(ConfigParseOptions options); - - /** - * Returns a {@link ConfigOrigin} describing the origin of the parseable - * item. - * @return the origin of the parseable item - */ - ConfigOrigin origin(); - - /** - * Get the initial options, which can be modified then passed to parse(). - * These options will have the right description, includer, and other - * parameters already set up. - * @return the initial options - */ - ConfigParseOptions options(); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigRenderOptions.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigRenderOptions.java deleted file mode 100644 index c131ad2c92..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigRenderOptions.java +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - * THIS FILE HAS BEEN MODIFIED TO ADD SUPPORT FOR COMPACT KEYS - */ -package com.gu.typesafe.config; - -/** - *

- * A set of options related to rendering a {@link ConfigValue}. Passed to - * {@link ConfigValue#render(ConfigRenderOptions)}. - * - *

- * Here is an example of creating a {@code ConfigRenderOptions}: - * - *

- *     ConfigRenderOptions options =
- *         ConfigRenderOptions.defaults().setComments(false)
- * 
- */ -public final class ConfigRenderOptions { - private final boolean originComments; - private final boolean comments; - private final boolean formatted; - private final boolean json; - private final boolean compactKeys; - - private ConfigRenderOptions(boolean originComments, boolean comments, boolean formatted, - boolean json, boolean compactKeys) { - this.originComments = originComments; - this.comments = comments; - this.formatted = formatted; - this.json = json; - this.compactKeys = compactKeys; - } - - /** - * Returns the default render options which are verbose (commented and - * formatted). See {@link ConfigRenderOptions#concise} for stripped-down - * options. This rendering will not be valid JSON since it has comments. - * - * @return the default render options - */ - public static ConfigRenderOptions defaults() { - return new ConfigRenderOptions(true, true, true, true, false); - } - - /** - * Returns concise render options (no whitespace or comments). For a - * resolved {@link Config}, the concise rendering will be valid JSON. - * - * @return the concise render options - */ - public static ConfigRenderOptions concise() { - return new ConfigRenderOptions(false, false, false, true, false); - } - - /** - * Returns options with comments toggled. This controls human-written - * comments but not the autogenerated "origin of this setting" comments, - * which are controlled by {@link ConfigRenderOptions#setOriginComments}. - * - * @param value - * true to include comments in the render - * @return options with requested setting for comments - */ - public ConfigRenderOptions setComments(boolean value) { - if (value == comments) - return this; - else - return new ConfigRenderOptions(originComments, value, formatted, json, compactKeys); - } - - /** - * Returns whether the options enable comments. This method is mostly used - * by the config lib internally, not by applications. - * - * @return true if comments should be rendered - */ - public boolean getComments() { - return comments; - } - - /** - * Returns options with origin comments toggled. If this is enabled, the - * library generates comments for each setting based on the - * {@link ConfigValue#origin} of that setting's value. For example these - * comments might tell you which file a setting comes from. - * - *

- * {@code setOriginComments()} controls only these autogenerated - * "origin of this setting" comments, to toggle regular comments use - * {@link ConfigRenderOptions#setComments}. - * - * @param value - * true to include autogenerated setting-origin comments in the - * render - * @return options with origin comments toggled - */ - public ConfigRenderOptions setOriginComments(boolean value) { - if (value == originComments) - return this; - else - return new ConfigRenderOptions(value, comments, formatted, json, compactKeys); - } - - /** - * Returns whether the options enable automated origin comments. This method - * is mostly used by the config lib internally, not by applications. - * - * @return true if origin comments should be rendered - */ - public boolean getOriginComments() { - return originComments; - } - - /** - * Returns options with formatting toggled. Formatting means indentation and - * whitespace, enabling formatting makes things prettier but larger. - * - * @param value - * true to enable formatting - * @return options with requested setting for formatting - */ - public ConfigRenderOptions setFormatted(boolean value) { - if (value == formatted) - return this; - else - return new ConfigRenderOptions(originComments, comments, value, json, compactKeys); - } - - /** - * Returns whether the options enable formatting. This method is mostly used - * by the config lib internally, not by applications. - * - * @return true if the options enable formatting - */ - public boolean getFormatted() { - return formatted; - } - - /** - * Returns options with JSON toggled. JSON means that HOCON extensions - * (omitting commas, quotes for example) won't be used. However, whether to - * use comments is controlled by the separate {@link #setComments(boolean)} - * and {@link #setOriginComments(boolean)} options. So if you enable - * comments you will get invalid JSON despite setting this to true. - * - * @param value - * true to include non-JSON extensions in the render - * @return options with requested setting for JSON - */ - public ConfigRenderOptions setJson(boolean value) { - if (value == json) - return this; - else - return new ConfigRenderOptions(originComments, comments, formatted, value, compactKeys); - } - - /** - * Returns whether the options enable JSON. This method is mostly used by - * the config lib internally, not by applications. - * - * @return true if only JSON should be rendered - */ - public boolean getJson() { - return json; - } - - /** - * Returns options with compact keys toggled. Compact keys means that when an - * object only has one member it will be rendered using the dot notation. - * - * i.e. rather than {@code key { key2=value }} you will get {@code key.key2=value} - * - * @param value - * true to compact the object keys - * @return options with requested setting for compact keys - */ - public ConfigRenderOptions setCompactKeys(boolean value) { - if (value == compactKeys) - return this; - else - return new ConfigRenderOptions(originComments, comments, formatted, json, value); - } - - /** - * Returns whether the options enable compact keys. This method is mostly used by - * the config lib internally, not by applications. - * - * @return true if object keys should be rendered compactly - */ - public boolean getCompactKeys() { - return compactKeys; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("ConfigRenderOptions("); - if (originComments) - sb.append("originComments,"); - if (comments) - sb.append("comments,"); - if (formatted) - sb.append("formatted,"); - if (json) - sb.append("json,"); - if (compactKeys) - sb.append("compactKeys,"); - if (sb.charAt(sb.length() - 1) == ',') - sb.setLength(sb.length() - 1); - sb.append(")"); - return sb.toString(); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigResolveOptions.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigResolveOptions.java deleted file mode 100644 index a0eb7613f5..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigResolveOptions.java +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -/** - * A set of options related to resolving substitutions. Substitutions use the - * ${foo.bar} syntax and are documented in the HOCON - * spec. - *

- * Typically this class would be used with the method - * {@link Config#resolve(ConfigResolveOptions)}. - *

- * This object is immutable, so the "setters" return a new object. - *

- * Here is an example of creating a custom {@code ConfigResolveOptions}: - * - *

- *     ConfigResolveOptions options = ConfigResolveOptions.defaults()
- *         .setUseSystemEnvironment(false)
- * 
- *

- * In addition to {@link ConfigResolveOptions#defaults}, there's a prebuilt - * {@link ConfigResolveOptions#noSystem} which avoids looking at any system - * environment variables or other external system information. (Right now, - * environment variables are the only example.) - */ -public final class ConfigResolveOptions { - private final boolean useSystemEnvironment; - private final boolean allowUnresolved; - private final ConfigResolver resolver; - - private ConfigResolveOptions(boolean useSystemEnvironment, boolean allowUnresolved, - ConfigResolver resolver) { - this.useSystemEnvironment = useSystemEnvironment; - this.allowUnresolved = allowUnresolved; - this.resolver = resolver; - } - - /** - * Returns the default resolve options. By default the system environment - * will be used and unresolved substitutions are not allowed. - * - * @return the default resolve options - */ - public static ConfigResolveOptions defaults() { - return new ConfigResolveOptions(true, false, NULL_RESOLVER); - } - - /** - * Returns resolve options that disable any reference to "system" data - * (currently, this means environment variables). - * - * @return the resolve options with env variables disabled - */ - public static ConfigResolveOptions noSystem() { - return defaults().setUseSystemEnvironment(false); - } - - /** - * Returns options with use of environment variables set to the given value. - * - * @param value - * true to resolve substitutions falling back to environment - * variables. - * @return options with requested setting for use of environment variables - */ - public ConfigResolveOptions setUseSystemEnvironment(boolean value) { - return new ConfigResolveOptions(value, allowUnresolved, resolver); - } - - /** - * Returns whether the options enable use of system environment variables. - * This method is mostly used by the config lib internally, not by - * applications. - * - * @return true if environment variables should be used - */ - public boolean getUseSystemEnvironment() { - return useSystemEnvironment; - } - - /** - * Returns options with "allow unresolved" set to the given value. By - * default, unresolved substitutions are an error. If unresolved - * substitutions are allowed, then a future attempt to use the unresolved - * value may fail, but {@link Config#resolve(ConfigResolveOptions)} itself - * will not throw. - * - * @param value - * true to silently ignore unresolved substitutions. - * @return options with requested setting for whether to allow substitutions - * @since 1.2.0 - */ - public ConfigResolveOptions setAllowUnresolved(boolean value) { - return new ConfigResolveOptions(useSystemEnvironment, value, resolver); - } - - /** - * Returns options where the given resolver used as a fallback if a - * reference cannot be otherwise resolved. This resolver will only be called - * after resolution has failed to substitute with a value from within the - * config itself and with any other resolvers that have been appended before - * this one. Multiple resolvers can be added using, - * - *

-     *     ConfigResolveOptions options = ConfigResolveOptions.defaults()
-     *         .appendResolver(primary)
-     *         .appendResolver(secondary)
-     *         .appendResolver(tertiary);
-     * 
- * - * With this config unresolved references will first be resolved with the - * primary resolver, if that fails then the secondary, and finally if that - * also fails the tertiary. - * - * If all fallbacks fail to return a substitution "allow unresolved" - * determines whether resolution fails or continues. - *` - * @param value the resolver to fall back to - * @return options that use the given resolver as a fallback - * @since 1.3.2 - */ - public ConfigResolveOptions appendResolver(ConfigResolver value) { - if (value == null) { - throw new ConfigException.BugOrBroken("null resolver passed to appendResolver"); - } else if (value == this.resolver) { - return this; - } else { - return new ConfigResolveOptions(useSystemEnvironment, allowUnresolved, - this.resolver.withFallback(value)); - } - } - - /** - * Returns the resolver to use as a fallback if a substitution cannot be - * otherwise resolved. Never returns null. This method is mostly used by the - * config lib internally, not by applications. - * - * @return the non-null fallback resolver - * @since 1.3.2 - */ - public ConfigResolver getResolver() { - return this.resolver; - } - - /** - * Returns whether the options allow unresolved substitutions. This method - * is mostly used by the config lib internally, not by applications. - * - * @return true if unresolved substitutions are allowed - * @since 1.2.0 - */ - public boolean getAllowUnresolved() { - return allowUnresolved; - } - - /** - * Singleton resolver that never resolves paths. - */ - private static final ConfigResolver NULL_RESOLVER = new ConfigResolver() { - - @Override - public ConfigValue lookup(String path) { - return null; - } - - @Override - public ConfigResolver withFallback(ConfigResolver fallback) { - return fallback; - } - - }; - -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigResolver.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigResolver.java deleted file mode 100644 index 3771d9db05..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigResolver.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gu.typesafe.config; - -/** - * Implement this interface and provide an instance to - * {@link ConfigResolveOptions#appendResolver ConfigResolveOptions.appendResolver()} - * to provide custom behavior when unresolved substitutions are encountered - * during resolution. - * @since 1.3.2 - */ -public interface ConfigResolver { - - /** - * Returns the value to substitute for the given unresolved path. To get the - * components of the path use {@link ConfigUtil#splitPath(String)}. If a - * non-null value is returned that value will be substituted, otherwise - * resolution will continue to consider the substitution as still - * unresolved. - * - * @param path the unresolved path - * @return the value to use as a substitution or null - */ - public ConfigValue lookup(String path); - - /** - * Returns a new resolver that falls back to the given resolver if this - * one doesn't provide a substitution itself. - * - * It's important to handle the case where you already have the fallback - * with a "return this", i.e. this method should not create a new object if - * the fallback is the same one you already have. The same fallback may be - * added repeatedly. - * - * @param fallback the previous includer for chaining - * @return a new resolver - */ - public ConfigResolver withFallback(ConfigResolver fallback); - -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigSyntax.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigSyntax.java deleted file mode 100644 index e30da93def..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigSyntax.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -/** - * The syntax of a character stream (JSON, HOCON - * aka ".conf", or Java properties). - * - */ -public enum ConfigSyntax { - /** - * Pedantically strict JSON format; no - * comments, no unexpected commas, no duplicate keys in the same object. - * Associated with the .json file extension and - * application/json Content-Type. - */ - JSON, - /** - * The JSON-superset HOCON format. Associated with the .conf file extension - * and application/hocon Content-Type. - */ - CONF, - /** - * Standard Java properties format. Associated with the .properties - * file extension and text/x-java-properties Content-Type. - */ - PROPERTIES; -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigUtil.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigUtil.java deleted file mode 100644 index 2b1f089a7c..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigUtil.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.gu.typesafe.config; - -import java.util.List; - -import com.gu.typesafe.config.impl.ConfigImplUtil; - -/** - * Contains static utility methods. - * - */ -public final class ConfigUtil { - private ConfigUtil() { - - } - - /** - * Quotes and escapes a string, as in the JSON specification. - * - * @param s - * a string - * @return the string quoted and escaped - */ - public static String quoteString(String s) { - return ConfigImplUtil.renderJsonString(s); - } - - /** - * Converts a list of keys to a path expression, by quoting the path - * elements as needed and then joining them separated by a period. A path - * expression is usable with a {@link Config}, while individual path - * elements are usable with a {@link ConfigObject}. - *

- * See the overview documentation for {@link Config} for more detail on path - * expressions vs. keys. - * - * @param elements - * the keys in the path - * @return a path expression - * @throws ConfigException - * if there are no elements - */ - public static String joinPath(String... elements) { - return ConfigImplUtil.joinPath(elements); - } - - /** - * Converts a list of strings to a path expression, by quoting the path - * elements as needed and then joining them separated by a period. A path - * expression is usable with a {@link Config}, while individual path - * elements are usable with a {@link ConfigObject}. - *

- * See the overview documentation for {@link Config} for more detail on path - * expressions vs. keys. - * - * @param elements - * the keys in the path - * @return a path expression - * @throws ConfigException - * if the list is empty - */ - public static String joinPath(List elements) { - return ConfigImplUtil.joinPath(elements); - } - - /** - * Converts a path expression into a list of keys, by splitting on period - * and unquoting the individual path elements. A path expression is usable - * with a {@link Config}, while individual path elements are usable with a - * {@link ConfigObject}. - *

- * See the overview documentation for {@link Config} for more detail on path - * expressions vs. keys. - * - * @param path - * a path expression - * @return the individual keys in the path - * @throws ConfigException - * if the path expression is invalid - */ - public static List splitPath(String path) { - return ConfigImplUtil.splitPath(path); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigValue.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigValue.java deleted file mode 100644 index df89cc4e36..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigValue.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -/** - * An immutable value, following the JSON type - * schema. - * - *

- * Because this object is immutable, it is safe to use from multiple threads and - * there's no need for "defensive copies." - * - *

- * Do not implement interface {@code ConfigValue}; it should only be - * implemented by the config library. Arbitrary implementations will not work - * because the library internals assume a specific concrete implementation. - * Also, this interface is likely to grow new methods over time, so third-party - * implementations will break. - */ -public interface ConfigValue extends com.gu.typesafe.config.ConfigMergeable { - /** - * The origin of the value (file, line number, etc.), for debugging and - * error messages. - * - * @return where the value came from - */ - ConfigOrigin origin(); - - /** - * The {@link ConfigValueType} of the value; matches the JSON type schema. - * - * @return value's type - */ - ConfigValueType valueType(); - - /** - * Returns the value as a plain Java boxed value, that is, a {@code String}, - * {@code Number}, {@code Boolean}, {@code Map}, - * {@code List}, or {@code null}, matching the {@link #valueType()} - * of this {@code ConfigValue}. If the value is a {@link ConfigObject} or - * {@link ConfigList}, it is recursively unwrapped. - * @return a plain Java value corresponding to this ConfigValue - */ - Object unwrapped(); - - /** - * Renders the config value as a HOCON string. This method is primarily - * intended for debugging, so it tries to add helpful comments and - * whitespace. - * - *

- * If the config value has not been resolved (see {@link Config#resolve}), - * it's possible that it can't be rendered as valid HOCON. In that case the - * rendering should still be useful for debugging but you might not be able - * to parse it. If the value has been resolved, it will always be parseable. - * - *

- * This method is equivalent to - * {@code render(ConfigRenderOptions.defaults())}. - * - * @return the rendered value - */ - String render(); - - /** - * Renders the config value to a string, using the provided options. - * - *

- * If the config value has not been resolved (see {@link Config#resolve}), - * it's possible that it can't be rendered as valid HOCON. In that case the - * rendering should still be useful for debugging but you might not be able - * to parse it. If the value has been resolved, it will always be parseable. - * - *

- * If the config value has been resolved and the options disable all - * HOCON-specific features (such as comments), the rendering will be valid - * JSON. If you enable HOCON-only features such as comments, the rendering - * will not be valid JSON. - * - * @param options - * the rendering options - * @return the rendered value - */ - String render(ConfigRenderOptions options); - - @Override - ConfigValue withFallback(ConfigMergeable other); - - /** - * Places the value inside a {@link Config} at the given path. See also - * {@link ConfigValue#atKey(String)}. - * - * @param path - * path to store this value at. - * @return a {@code Config} instance containing this value at the given - * path. - */ - Config atPath(String path); - - /** - * Places the value inside a {@link Config} at the given key. See also - * {@link ConfigValue#atPath(String)}. - * - * @param key - * key to store this value at. - * @return a {@code Config} instance containing this value at the given key. - */ - Config atKey(String key); - - /** - * Returns a {@code ConfigValue} based on this one, but with the given - * origin. This is useful when you are parsing a new format of file or setting - * comments for a single ConfigValue. - * - * @since 1.3.0 - * - * @param origin the origin set on the returned value - * @return the new ConfigValue with the given origin - */ - ConfigValue withOrigin(ConfigOrigin origin); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigValueFactory.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigValueFactory.java deleted file mode 100644 index c5e1393320..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigValueFactory.java +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -import java.util.Map; - -import com.gu.typesafe.config.impl.ConfigImpl; - -/** - * This class holds some static factory methods for building {@link ConfigValue} - * instances. See also {@link ConfigFactory} which has methods for parsing files - * and certain in-memory data structures. - */ -public final class ConfigValueFactory { - private ConfigValueFactory() { - } - - /** - * Creates a {@link ConfigValue} from a plain Java boxed value, which may be - * a Boolean, Number, String, - * Map, Iterable, or null. A - * Map must be a Map from String to more values - * that can be supplied to fromAnyRef(). An - * Iterable must iterate over more values that can be supplied - * to fromAnyRef(). A Map will become a - * {@link ConfigObject} and an Iterable will become a - * {@link com.gu.typesafe.config.ConfigList}. If the Iterable is not an ordered - * collection, results could be strange, since ConfigList is - * ordered. - * - *

- * In a Map passed to fromAnyRef(), the map's keys - * are plain keys, not path expressions. So if your Map has a - * key "foo.bar" then you will get one object with a key called "foo.bar", - * rather than an object with a key "foo" containing another object with a - * key "bar". - * - *

- * The originDescription will be used to set the origin() field on the - * ConfigValue. It should normally be the name of the file the values came - * from, or something short describing the value such as "default settings". - * The originDescription is prefixed to error messages so users can tell - * where problematic values are coming from. - * - *

- * Supplying the result of ConfigValue.unwrapped() to this function is - * guaranteed to work and should give you back a ConfigValue that matches - * the one you unwrapped. The re-wrapped ConfigValue will lose some - * information that was present in the original such as its origin, but it - * will have matching values. - * - *

- * If you pass in a ConfigValue to this - * function, it will be returned unmodified. (The - * originDescription will be ignored in this - * case.) - * - *

- * This function throws if you supply a value that cannot be converted to a - * ConfigValue, but supplying such a value is a bug in your program, so you - * should never handle the exception. Just fix your program (or report a bug - * against this library). - * - * @param object - * object to convert to ConfigValue - * @param originDescription - * name of origin file or brief description of what the value is - * @return a new value - */ - public static ConfigValue fromAnyRef(Object object, String originDescription) { - return ConfigImpl.fromAnyRef(object, originDescription); - } - - /** - * See the {@link #fromAnyRef(Object,String)} documentation for details. - * This is a typesafe wrapper that only works on {@link java.util.Map} and - * returns {@link ConfigObject} rather than {@link ConfigValue}. - * - *

- * If your Map has a key "foo.bar" then you will get one object - * with a key called "foo.bar", rather than an object with a key "foo" - * containing another object with a key "bar". The keys in the map are keys; - * not path expressions. That is, the Map corresponds exactly - * to a single {@code ConfigObject}. The keys will not be parsed or - * modified, and the values are wrapped in ConfigValue. To get nested - * {@code ConfigObject}, some of the values in the map would have to be more - * maps. - * - *

- * See also {@link ConfigFactory#parseMap(Map,String)} which interprets the - * keys in the map as path expressions. - * - * @param values map from keys to plain Java values - * @param originDescription description to use in {@link ConfigOrigin} of created values - * @return a new {@link ConfigObject} value - */ - public static ConfigObject fromMap(Map values, - String originDescription) { - return (ConfigObject) fromAnyRef(values, originDescription); - } - - /** - * See the {@link #fromAnyRef(Object,String)} documentation for details. - * This is a typesafe wrapper that only works on {@link java.lang.Iterable} - * and returns {@link com.gu.typesafe.config.ConfigList} rather than {@link ConfigValue}. - * - * @param values list of plain Java values - * @param originDescription description to use in {@link ConfigOrigin} of created values - * @return a new {@link com.gu.typesafe.config.ConfigList} value - */ - public static com.gu.typesafe.config.ConfigList fromIterable(Iterable values, - String originDescription) { - return (com.gu.typesafe.config.ConfigList) fromAnyRef(values, originDescription); - } - - /** - * See the other overload {@link #fromAnyRef(Object,String)} for details, - * this one just uses a default origin description. - * - * @param object a plain Java value - * @return a new {@link ConfigValue} - */ - public static ConfigValue fromAnyRef(Object object) { - return fromAnyRef(object, null); - } - - /** - * See the other overload {@link #fromMap(Map,String)} for details, this one - * just uses a default origin description. - * - *

- * See also {@link ConfigFactory#parseMap(Map)} which interprets the keys in - * the map as path expressions. - * - * @param values map from keys to plain Java values - * @return a new {@link ConfigObject} - */ - public static ConfigObject fromMap(Map values) { - return fromMap(values, null); - } - - /** - * See the other overload of {@link #fromIterable(Iterable, String)} for - * details, this one just uses a default origin description. - * - * @param values list of plain Java values - * @return a new {@link com.gu.typesafe.config.ConfigList} - */ - public static ConfigList fromIterable(Iterable values) { - return fromIterable(values, null); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/ConfigValueType.java b/scripts/src/main/java/com/gu/typesafe/config/ConfigValueType.java deleted file mode 100644 index 828b6b2095..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/ConfigValueType.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config; - -/** - * The type of a configuration value (following the JSON type schema). - */ -public enum ConfigValueType { - OBJECT, LIST, NUMBER, BOOLEAN, NULL, STRING -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/DefaultConfigLoadingStrategy.java b/scripts/src/main/java/com/gu/typesafe/config/DefaultConfigLoadingStrategy.java deleted file mode 100644 index 253c7b85b3..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/DefaultConfigLoadingStrategy.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gu.typesafe.config; - -/** - * Default config loading strategy. Able to load resource, file or URL. - * Behavior may be altered by defining one of VM properties - * {@code config.resource}, {@code config.file} or {@code config.url} - */ -public class DefaultConfigLoadingStrategy implements ConfigLoadingStrategy { - @Override - public Config parseApplicationConfig(ConfigParseOptions parseOptions) { - return com.gu.typesafe.config.ConfigFactory.parseApplicationReplacement(parseOptions) - .orElseGet(() -> ConfigFactory.parseResourcesAnySyntax("application", parseOptions)); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/Optional.java b/scripts/src/main/java/com/gu/typesafe/config/Optional.java deleted file mode 100644 index f0c6a5386a..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/Optional.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gu.typesafe.config; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Allows an config property to be {@code null}. - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -public @interface Optional { - -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigNode.java b/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigNode.java deleted file mode 100644 index 4d9c2e5371..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigNode.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.parser.ConfigNode; - -import java.util.Collection; - -abstract class AbstractConfigNode implements ConfigNode { - abstract Collection tokens(); - final public String render() { - StringBuilder origText = new StringBuilder(); - Iterable tokens = tokens(); - for (Token t : tokens) { - origText.append(t.tokenText()); - } - return origText.toString(); - } - - @Override - final public boolean equals(Object other) { - return other instanceof AbstractConfigNode && render().equals(((AbstractConfigNode)other).render()); - } - - @Override - final public int hashCode() { - return render().hashCode(); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigNodeValue.java b/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigNodeValue.java deleted file mode 100644 index cc01d896cc..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigNodeValue.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -// This is required if we want -// to be referencing the AbstractConfigNode class in implementation rather than the -// ConfigNode interface, as we can't cast an AbstractConfigNode to an interface -abstract class AbstractConfigNodeValue extends AbstractConfigNode { - -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigObject.java b/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigObject.java deleted file mode 100644 index 2e1d3550fb..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigObject.java +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import com.gu.typesafe.config.*; -import com.gu.typesafe.config.ConfigMergeable; -import com.gu.typesafe.config.ConfigRenderOptions; - -abstract class AbstractConfigObject extends com.gu.typesafe.config.impl.AbstractConfigValue implements com.gu.typesafe.config.ConfigObject, com.gu.typesafe.config.impl.Container { - final private com.gu.typesafe.config.impl.SimpleConfig config; - - protected AbstractConfigObject(com.gu.typesafe.config.ConfigOrigin origin) { - super(origin); - this.config = new com.gu.typesafe.config.impl.SimpleConfig(this); - } - - @Override - public com.gu.typesafe.config.impl.SimpleConfig toConfig() { - return config; - } - - @Override - public AbstractConfigObject toFallbackValue() { - return this; - } - - @Override - abstract public AbstractConfigObject withOnlyKey(String key); - - @Override - abstract public AbstractConfigObject withoutKey(String key); - - @Override - abstract public AbstractConfigObject withValue(String key, com.gu.typesafe.config.ConfigValue value); - - abstract protected AbstractConfigObject withOnlyPathOrNull(Path path); - - abstract AbstractConfigObject withOnlyPath(Path path); - - abstract AbstractConfigObject withoutPath(Path path); - - abstract AbstractConfigObject withValue(Path path, com.gu.typesafe.config.ConfigValue value); - - /** - * This looks up the key with no transformation or type conversion of any - * kind, and returns null if the key is not present. The object must be - * resolved along the nodes needed to get the key or - * ConfigException.NotResolved will be thrown. - * - * @param key - * @return the unmodified raw value or null - */ - protected final com.gu.typesafe.config.impl.AbstractConfigValue peekAssumingResolved(String key, Path originalPath) { - try { - return attemptPeekWithPartialResolve(key); - } catch (com.gu.typesafe.config.ConfigException.NotResolved e) { - throw com.gu.typesafe.config.impl.ConfigImpl.improveNotResolved(originalPath, e); - } - } - - /** - * Look up the key on an only-partially-resolved object, with no - * transformation or type conversion of any kind; if 'this' is not resolved - * then try to look up the key anyway if possible. - * - * @param key - * key to look up - * @return the value of the key, or null if known not to exist - * @throws com.gu.typesafe.config.ConfigException.NotResolved - * if can't figure out key's value (or existence) without more - * resolving - */ - abstract com.gu.typesafe.config.impl.AbstractConfigValue attemptPeekWithPartialResolve(String key); - - /** - * Looks up the path with no transformation or type conversion. Returns null - * if the path is not found; throws ConfigException.NotResolved if we need - * to go through an unresolved node to look up the path. - */ - protected com.gu.typesafe.config.impl.AbstractConfigValue peekPath(Path path) { - return peekPath(this, path); - } - - private static com.gu.typesafe.config.impl.AbstractConfigValue peekPath(AbstractConfigObject self, Path path) { - try { - // we'll fail if anything along the path can't - // be looked at without resolving. - Path next = path.remainder(); - com.gu.typesafe.config.impl.AbstractConfigValue v = self.attemptPeekWithPartialResolve(path.first()); - - if (next == null) { - return v; - } else { - if (v instanceof AbstractConfigObject) { - return peekPath((AbstractConfigObject) v, next); - } else { - return null; - } - } - } catch (com.gu.typesafe.config.ConfigException.NotResolved e) { - throw ConfigImpl.improveNotResolved(path, e); - } - } - - @Override - public com.gu.typesafe.config.ConfigValueType valueType() { - return ConfigValueType.OBJECT; - } - - protected abstract AbstractConfigObject newCopy(ResolveStatus status, com.gu.typesafe.config.ConfigOrigin origin); - - @Override - protected AbstractConfigObject newCopy(com.gu.typesafe.config.ConfigOrigin origin) { - return newCopy(resolveStatus(), origin); - } - - @Override - protected AbstractConfigObject constructDelayedMerge(com.gu.typesafe.config.ConfigOrigin origin, - List stack) { - return new com.gu.typesafe.config.impl.ConfigDelayedMergeObject(origin, stack); - } - - @Override - protected abstract AbstractConfigObject mergedWithObject(AbstractConfigObject fallback); - - @Override - public AbstractConfigObject withFallback(ConfigMergeable mergeable) { - return (AbstractConfigObject) super.withFallback(mergeable); - } - - static com.gu.typesafe.config.ConfigOrigin mergeOrigins( - Collection stack) { - if (stack.isEmpty()) - throw new ConfigException.BugOrBroken( - "can't merge origins on empty list"); - List origins = new ArrayList(); - com.gu.typesafe.config.ConfigOrigin firstOrigin = null; - int numMerged = 0; - for (com.gu.typesafe.config.impl.AbstractConfigValue v : stack) { - if (firstOrigin == null) - firstOrigin = v.origin(); - - if (v instanceof AbstractConfigObject - && ((AbstractConfigObject) v).resolveStatus() == ResolveStatus.RESOLVED - && ((ConfigObject) v).isEmpty()) { - // don't include empty files or the .empty() - // config in the description, since they are - // likely to be "implementation details" - } else { - origins.add(v.origin()); - numMerged += 1; - } - } - - if (numMerged == 0) { - // the configs were all empty, so just use the first one - origins.add(firstOrigin); - } - - return com.gu.typesafe.config.impl.SimpleConfigOrigin.mergeOrigins(origins); - } - - static com.gu.typesafe.config.ConfigOrigin mergeOrigins(AbstractConfigObject... stack) { - return mergeOrigins(Arrays.asList(stack)); - } - - @Override - abstract com.gu.typesafe.config.impl.ResolveResult resolveSubstitutions(com.gu.typesafe.config.impl.ResolveContext context, - com.gu.typesafe.config.impl.ResolveSource source) - throws NotPossibleToResolve; - - @Override - abstract AbstractConfigObject relativized(final Path prefix); - - @Override - public abstract com.gu.typesafe.config.impl.AbstractConfigValue get(Object key); - - @Override - protected abstract void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options); - - private static UnsupportedOperationException weAreImmutable(String method) { - return new UnsupportedOperationException("ConfigObject is immutable, you can't call Map." - + method); - } - - @Override - public void clear() { - throw weAreImmutable("clear"); - } - - @Override - public com.gu.typesafe.config.ConfigValue put(String arg0, com.gu.typesafe.config.ConfigValue arg1) { - throw weAreImmutable("put"); - } - - @Override - public void putAll(Map arg0) { - throw weAreImmutable("putAll"); - } - - @Override - public ConfigValue remove(Object arg0) { - throw weAreImmutable("remove"); - } - - @Override - public AbstractConfigObject withOrigin(ConfigOrigin origin) { - return (AbstractConfigObject) super.withOrigin(origin); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigValue.java b/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigValue.java deleted file mode 100644 index 36cd2300f2..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/AbstractConfigValue.java +++ /dev/null @@ -1,410 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - * THIS FILE HAS BEEN MODIFIED TO ADD SUPPORT FOR COMPACT KEYS - */ -package com.gu.typesafe.config.impl; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import com.gu.typesafe.config.*; - -/** - * - * Trying very hard to avoid a parent reference in config values; when you have - * a tree like this, the availability of parent() tends to result in a lot of - * improperly-factored and non-modular code. Please don't add parent(). - * - */ -abstract class AbstractConfigValue implements com.gu.typesafe.config.ConfigValue, MergeableValue { - - final private SimpleConfigOrigin origin; - - AbstractConfigValue(com.gu.typesafe.config.ConfigOrigin origin) { - this.origin = (SimpleConfigOrigin) origin; - } - - @Override - public SimpleConfigOrigin origin() { - return this.origin; - } - - /** - * This exception means that a value is inherently not resolveable, at the - * moment the only known cause is a cycle of substitutions. This is a - * checked exception since it's internal to the library and we want to be - * sure we handle it before passing it out to public API. This is only - * supposed to be thrown by the target of a cyclic reference and it's - * supposed to be caught by the ConfigReference looking up that reference, - * so it should be impossible for an outermost resolve() to throw this. - * - * Contrast with ConfigException.NotResolved which just means nobody called - * resolve(). - */ - static class NotPossibleToResolve extends Exception { - private static final long serialVersionUID = 1L; - - final private String traceString; - - NotPossibleToResolve(ResolveContext context) { - super("was not possible to resolve"); - this.traceString = context.traceString(); - } - - String traceString() { - return traceString; - } - } - - /** - * Called only by ResolveContext.resolve(). - * - * @param context - * state of the current resolve - * @param source - * where to look up values - * @return a new value if there were changes, or this if no changes - */ - ResolveResult resolveSubstitutions(ResolveContext context, ResolveSource source) - throws NotPossibleToResolve { - return ResolveResult.make(context, this); - } - - ResolveStatus resolveStatus() { - return ResolveStatus.RESOLVED; - } - - protected static List replaceChildInList(List list, - AbstractConfigValue child, AbstractConfigValue replacement) { - int i = 0; - while (i < list.size() && list.get(i) != child) - ++i; - if (i == list.size()) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("tried to replace " + child + " which is not in " + list); - List newStack = new ArrayList(list); - if (replacement != null) - newStack.set(i, replacement); - else - newStack.remove(i); - - if (newStack.isEmpty()) - return null; - else - return newStack; - } - - protected static boolean hasDescendantInList(List list, AbstractConfigValue descendant) { - for (AbstractConfigValue v : list) { - if (v == descendant) - return true; - } - // now the expensive traversal - for (AbstractConfigValue v : list) { - if (v instanceof Container && ((Container) v).hasDescendant(descendant)) - return true; - } - return false; - } - - /** - * This is used when including one file in another; the included file is - * relativized to the path it's included into in the parent file. The point - * is that if you include a file at foo.bar in the parent, and the included - * file as a substitution ${a.b.c}, the included substitution now needs to - * be ${foo.bar.a.b.c} because we resolve substitutions globally only after - * parsing everything. - * - * @param prefix - * @return value relativized to the given path or the same value if nothing - * to do - */ - AbstractConfigValue relativized(Path prefix) { - return this; - } - - protected interface Modifier { - // keyOrNull is null for non-objects - AbstractConfigValue modifyChildMayThrow(String keyOrNull, AbstractConfigValue v) - throws Exception; - } - - protected abstract class NoExceptionsModifier implements Modifier { - @Override - public final AbstractConfigValue modifyChildMayThrow(String keyOrNull, AbstractConfigValue v) - throws Exception { - try { - return modifyChild(keyOrNull, v); - } catch (RuntimeException e) { - throw e; - } catch(Exception e) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Unexpected exception", e); - } - } - - abstract AbstractConfigValue modifyChild(String keyOrNull, AbstractConfigValue v); - } - - @Override - public AbstractConfigValue toFallbackValue() { - return this; - } - - protected abstract AbstractConfigValue newCopy(com.gu.typesafe.config.ConfigOrigin origin); - - // this is virtualized rather than a field because only some subclasses - // really need to store the boolean, and they may be able to pack it - // with another boolean to save space. - protected boolean ignoresFallbacks() { - // if we are not resolved, then somewhere in this value there's - // a substitution that may need to look at the fallbacks. - return resolveStatus() == ResolveStatus.RESOLVED; - } - - protected AbstractConfigValue withFallbacksIgnored() { - if (ignoresFallbacks()) - return this; - else - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "value class doesn't implement forced fallback-ignoring " + this); - } - - // the withFallback() implementation is supposed to avoid calling - // mergedWith* if we're ignoring fallbacks. - protected final void requireNotIgnoringFallbacks() { - if (ignoresFallbacks()) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "method should not have been called with ignoresFallbacks=true " - + getClass().getSimpleName()); - } - - protected AbstractConfigValue constructDelayedMerge(com.gu.typesafe.config.ConfigOrigin origin, - List stack) { - return new ConfigDelayedMerge(origin, stack); - } - - protected final AbstractConfigValue mergedWithTheUnmergeable( - Collection stack, Unmergeable fallback) { - requireNotIgnoringFallbacks(); - - // if we turn out to be an object, and the fallback also does, - // then a merge may be required; delay until we resolve. - List newStack = new ArrayList(); - newStack.addAll(stack); - newStack.addAll(fallback.unmergedValues()); - return constructDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack); - } - - private final AbstractConfigValue delayMerge(Collection stack, - AbstractConfigValue fallback) { - // if we turn out to be an object, and the fallback also does, - // then a merge may be required. - // if we contain a substitution, resolving it may need to look - // back to the fallback. - List newStack = new ArrayList(); - newStack.addAll(stack); - newStack.add(fallback); - return constructDelayedMerge(AbstractConfigObject.mergeOrigins(newStack), newStack); - } - - protected final AbstractConfigValue mergedWithObject(Collection stack, - AbstractConfigObject fallback) { - requireNotIgnoringFallbacks(); - - if (this instanceof AbstractConfigObject) - throw new ConfigException.BugOrBroken("Objects must reimplement mergedWithObject"); - - return mergedWithNonObject(stack, fallback); - } - - protected final AbstractConfigValue mergedWithNonObject(Collection stack, - AbstractConfigValue fallback) { - requireNotIgnoringFallbacks(); - - if (resolveStatus() == ResolveStatus.RESOLVED) { - // falling back to a non-object doesn't merge anything, and also - // prohibits merging any objects that we fall back to later. - // so we have to switch to ignoresFallbacks mode. - return withFallbacksIgnored(); - } else { - // if unresolved, we may have to look back to fallbacks as part of - // the resolution process, so always delay - return delayMerge(stack, fallback); - } - } - - protected AbstractConfigValue mergedWithTheUnmergeable(Unmergeable fallback) { - requireNotIgnoringFallbacks(); - - return mergedWithTheUnmergeable(Collections.singletonList(this), fallback); - } - - protected AbstractConfigValue mergedWithObject(AbstractConfigObject fallback) { - requireNotIgnoringFallbacks(); - - return mergedWithObject(Collections.singletonList(this), fallback); - } - - protected AbstractConfigValue mergedWithNonObject(AbstractConfigValue fallback) { - requireNotIgnoringFallbacks(); - - return mergedWithNonObject(Collections.singletonList(this), fallback); - } - - @Override - public AbstractConfigValue withOrigin(com.gu.typesafe.config.ConfigOrigin origin) { - if (this.origin == origin) - return this; - else - return newCopy(origin); - } - - // this is only overridden to change the return type - @Override - public AbstractConfigValue withFallback(ConfigMergeable mergeable) { - if (ignoresFallbacks()) { - return this; - } else { - com.gu.typesafe.config.ConfigValue other = ((MergeableValue) mergeable).toFallbackValue(); - - if (other instanceof Unmergeable) { - return mergedWithTheUnmergeable((Unmergeable) other); - } else if (other instanceof AbstractConfigObject) { - return mergedWithObject((AbstractConfigObject) other); - } else { - return mergedWithNonObject((AbstractConfigValue) other); - } - } - } - - protected boolean canEqual(Object other) { - return other instanceof com.gu.typesafe.config.ConfigValue; - } - - @Override - public boolean equals(Object other) { - // note that "origin" is deliberately NOT part of equality - if (other instanceof com.gu.typesafe.config.ConfigValue) { - return canEqual(other) - && (this.valueType() == - ((com.gu.typesafe.config.ConfigValue) other).valueType()) - && ConfigImplUtil.equalsHandlingNull(this.unwrapped(), - ((ConfigValue) other).unwrapped()); - } else { - return false; - } - } - - @Override - public int hashCode() { - // note that "origin" is deliberately NOT part of equality - Object o = this.unwrapped(); - if (o == null) - return 0; - else - return o.hashCode(); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - render(sb, 0, true /* atRoot */, null /* atKey */, com.gu.typesafe.config.ConfigRenderOptions.concise()); - return getClass().getSimpleName() + "(" + sb.toString() + ")"; - } - - protected static void indent(StringBuilder sb, int indent, com.gu.typesafe.config.ConfigRenderOptions options) { - if (options.getFormatted()) { - int remaining = indent; - while (remaining > 0) { - sb.append(" "); - --remaining; - } - } - } - - protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, com.gu.typesafe.config.ConfigRenderOptions options) { - if (atKey != null) { - String renderedKey; - if (options.getJson()) - renderedKey = ConfigImplUtil.renderJsonString(atKey); - else - renderedKey = ConfigImplUtil.renderStringUnquotedIfPossible(atKey); - - sb.append(renderedKey); - - if (options.getJson()) { - if (options.getFormatted()) - sb.append(" : "); - else - sb.append(":"); - } else { - // in non-JSON we can omit the colon or equals before an object - if (this instanceof ConfigObject) { - ConfigObject configObj = (ConfigObject) this; - boolean singleton = configObj.keySet().size() == 1; - boolean shouldCompact = singleton && options.getCompactKeys(); - if (options.getFormatted() && !shouldCompact) - sb.append(' '); - } else { - sb.append("="); - } - } - } - render(sb, indent, atRoot, options); - } - - protected void render(StringBuilder sb, int indent, boolean atRoot, com.gu.typesafe.config.ConfigRenderOptions options) { - Object u = unwrapped(); - sb.append(u.toString()); - } - - @Override - public final String render() { - return render(com.gu.typesafe.config.ConfigRenderOptions.defaults()); - } - - @Override - public final String render(ConfigRenderOptions options) { - StringBuilder sb = new StringBuilder(); - render(sb, 0, true, null, options); - return sb.toString(); - } - - // toString() is a debugging-oriented string but this is defined - // to create a string that would parse back to the value in JSON. - // It only works for primitive values (that would be a single token) - // which are auto-converted to strings when concatenating with - // other strings or by the DefaultTransformer. - String transformToString() { - return null; - } - - SimpleConfig atKey(com.gu.typesafe.config.ConfigOrigin origin, String key) { - Map m = Collections.singletonMap(key, this); - return (new SimpleConfigObject(origin, m)).toConfig(); - } - - @Override - public SimpleConfig atKey(String key) { - return atKey(SimpleConfigOrigin.newSimple("atKey(" + key + ")"), key); - } - - SimpleConfig atPath(ConfigOrigin origin, Path path) { - Path parent = path.parent(); - SimpleConfig result = atKey(origin, path.last()); - while (parent != null) { - String key = parent.last(); - result = result.atKey(origin, key); - parent = parent.parent(); - } - return result; - } - - @Override - public SimpleConfig atPath(String pathExpression) { - SimpleConfigOrigin origin = SimpleConfigOrigin.newSimple("atPath(" + pathExpression + ")"); - return atPath(origin, Path.newPath(pathExpression)); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/BadMap.java b/scripts/src/main/java/com/gu/typesafe/config/impl/BadMap.java deleted file mode 100644 index a0b68a8e52..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/BadMap.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.gu.typesafe.config.impl; - -/** - * A terrible Map that isn't as expensive as HashMap to copy and - * add one item to. Please write something real if you see this - * and get cranky. - */ -final class BadMap { - final static class Entry { - final int hash; - final Object key; - final Object value; - final Entry next; - - Entry(int hash, Object k, Object v, Entry next) { - this.hash = hash; - this.key = k; - this.value = v; - this.next = next; - } - - Object find(Object k) { - if (key.equals(k)) - return value; - else if (next != null) - return next.find(k); - else - return null; - } - } - - private final int size; - private final Entry[] entries; - - private final static Entry[] emptyEntries = {}; - - BadMap() { - this(0, emptyEntries); - } - - private BadMap(int size, Entry[] entries) { - this.size = size; - this.entries = entries; - } - - BadMap copyingPut(K k, V v) { - int newSize = size + 1; - Entry[] newEntries; - if (newSize > entries.length) { - // nextPrime doesn't always return a prime larger than - // we passed in, so this block may not actually change - // the entries size. the "-1" is to ensure we use - // array length 2 when going from 0 to 1. - newEntries = new Entry[nextPrime((newSize * 2) - 1)]; - } else { - newEntries = new Entry[entries.length]; - } - - if (newEntries.length == entries.length) { - System.arraycopy(entries, 0, newEntries, 0, entries.length); - } else { - rehash(entries, newEntries); - } - - int hash = Math.abs(k.hashCode()); - store(newEntries, hash, k, v); - return new BadMap<>(newSize, newEntries); - } - - private static void store(Entry[] entries, int hash, K k, V v) { - int i = hash % entries.length; - Entry old = entries[i]; // old may be null - entries[i] = new Entry(hash, k, v, old); - } - - private static void store(Entry[] entries, Entry e) { - int i = e.hash % entries.length; - Entry old = entries[i]; // old may be null - if (old == null && e.next == null) { - // share the entry since it has no "next" - entries[i] = e; - } else { - // bah, have to copy it - entries[i] = new Entry(e.hash, e.key, e.value, old); - } - } - - private static void rehash(Entry[] src, Entry[] dest) { - for (Entry entry : src) { - // have to store each "next" element individually; they may belong in different indices - while (entry != null) { - store(dest, entry); - entry = entry.next; - } - } - } - - @SuppressWarnings("unchecked") - V get(K k) { - if (entries.length == 0) { - return null; - } else { - int hash = Math.abs(k.hashCode()); - int i = hash % entries.length; - Entry e = entries[i]; - if (e == null) - return null; - else - return (V) e.find(k); - } - } - - - private final static int[] primes = { - /* Skip some early ones that are close together */ - 2, /* 3, */ 5, /* 7, */ 11, /* 13, */ 17, /* 19, */ 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, - 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, - 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, - 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, - 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, - 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, - 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, - 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, - 947, 953, 967, 971, 977, 983, 991, 997, 1009, - /* now we start skipping some, this is arbitrary */ - 2053, 3079, 4057, 7103, 10949, 16069, 32609, 65867, 104729 - }; - - private static int nextPrime(int i) { - for (int p : primes) { - if (p > i) - return p; - } - /* oh well */ - return primes[primes.length - 1]; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigBeanImpl.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigBeanImpl.java deleted file mode 100644 index 9acfc1223e..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigBeanImpl.java +++ /dev/null @@ -1,299 +0,0 @@ -package com.gu.typesafe.config.impl; - -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.time.Duration; -import java.util.Set; - -import com.gu.typesafe.config.*; -import com.gu.typesafe.config.ConfigMemorySize; - -/** - * Internal implementation detail, not ABI stable, do not touch. - * For use only by the {@link com.typesafe.config} package. - */ -public class ConfigBeanImpl { - - /** - * This is public ONLY for use by the "config" package, DO NOT USE this ABI - * may change. - * @param type of the bean - * @param config config to use - * @param clazz class of the bean - * @return the bean instance - */ - public static T createInternal(com.gu.typesafe.config.Config config, Class clazz) { - if (((com.gu.typesafe.config.impl.SimpleConfig)config).root().resolveStatus() != ResolveStatus.RESOLVED) - throw new com.gu.typesafe.config.ConfigException.NotResolved( - "need to Config#resolve() a config before using it to initialize a bean, see the API docs for Config#resolve()"); - - Map configProps = new HashMap(); - Map originalNames = new HashMap(); - for (Map.Entry configProp : config.root().entrySet()) { - String originalName = configProp.getKey(); - String camelName = ConfigImplUtil.toCamelCase(originalName); - // if a setting is in there both as some hyphen name and the camel name, - // the camel one wins - if (originalNames.containsKey(camelName) && !originalName.equals(camelName)) { - // if we aren't a camel name to start with, we lose. - // if we are or we are the first matching key, we win. - } else { - configProps.put(camelName, (com.gu.typesafe.config.impl.AbstractConfigValue) configProp.getValue()); - originalNames.put(camelName, originalName); - } - } - - BeanInfo beanInfo = null; - try { - beanInfo = Introspector.getBeanInfo(clazz); - } catch (IntrospectionException e) { - throw new com.gu.typesafe.config.ConfigException.BadBean("Could not get bean information for class " + clazz.getName(), e); - } - - try { - List beanProps = new ArrayList(); - for (PropertyDescriptor beanProp : beanInfo.getPropertyDescriptors()) { - if (beanProp.getReadMethod() == null || beanProp.getWriteMethod() == null) { - continue; - } - beanProps.add(beanProp); - } - - // Try to throw all validation issues at once (this does not comprehensively - // find every issue, but it should find common ones). - List problems = new ArrayList(); - for (PropertyDescriptor beanProp : beanProps) { - Method setter = beanProp.getWriteMethod(); - Class parameterClass = setter.getParameterTypes()[0]; - - com.gu.typesafe.config.ConfigValueType expectedType = getValueTypeOrNull(parameterClass); - if (expectedType != null) { - String name = originalNames.get(beanProp.getName()); - if (name == null) - name = beanProp.getName(); - Path path = Path.newKey(name); - com.gu.typesafe.config.impl.AbstractConfigValue configValue = configProps.get(beanProp.getName()); - if (configValue != null) { - com.gu.typesafe.config.impl.SimpleConfig.checkValid(path, expectedType, configValue, problems); - } else { - if (!isOptionalProperty(clazz, beanProp)) { - com.gu.typesafe.config.impl.SimpleConfig.addMissing(problems, expectedType, path, config.origin()); - } - } - } - } - - if (!problems.isEmpty()) { - throw new com.gu.typesafe.config.ConfigException.ValidationFailed(problems); - } - - // Fill in the bean instance - T bean = clazz.newInstance(); - for (PropertyDescriptor beanProp : beanProps) { - Method setter = beanProp.getWriteMethod(); - Type parameterType = setter.getGenericParameterTypes()[0]; - Class parameterClass = setter.getParameterTypes()[0]; - String configPropName = originalNames.get(beanProp.getName()); - // Is the property key missing in the config? - if (configPropName == null) { - // If so, continue if the field is marked as @{link Optional} - if (isOptionalProperty(clazz, beanProp)) { - continue; - } - // Otherwise, raise a {@link Missing} exception right here - throw new com.gu.typesafe.config.ConfigException.Missing(beanProp.getName()); - } - Object unwrapped = getValue(clazz, parameterType, parameterClass, config, configPropName); - setter.invoke(bean, unwrapped); - } - return bean; - } catch (InstantiationException e) { - throw new com.gu.typesafe.config.ConfigException.BadBean(clazz.getName() + " needs a public no-args constructor to be used as a bean", e); - } catch (IllegalAccessException e) { - throw new com.gu.typesafe.config.ConfigException.BadBean(clazz.getName() + " getters and setters are not accessible, they must be for use as a bean", e); - } catch (InvocationTargetException e) { - throw new com.gu.typesafe.config.ConfigException.BadBean("Calling bean method on " + clazz.getName() + " caused an exception", e); - } - } - - // we could magically make this work in many cases by doing - // getAnyRef() (or getValue().unwrapped()), but anytime we - // rely on that, we aren't doing the type conversions Config - // usually does, and we will throw ClassCastException instead - // of a nicer error message giving the name of the bad - // setting. So, instead, we only support a limited number of - // types plus you can always use Object, ConfigValue, Config, - // ConfigObject, etc. as an escape hatch. - private static Object getValue(Class beanClass, Type parameterType, Class parameterClass, com.gu.typesafe.config.Config config, - String configPropName) { - if (parameterClass == Boolean.class || parameterClass == boolean.class) { - return config.getBoolean(configPropName); - } else if (parameterClass == Integer.class || parameterClass == int.class) { - return config.getInt(configPropName); - } else if (parameterClass == Double.class || parameterClass == double.class) { - return config.getDouble(configPropName); - } else if (parameterClass == Long.class || parameterClass == long.class) { - return config.getLong(configPropName); - } else if (parameterClass == String.class) { - return config.getString(configPropName); - } else if (parameterClass == Duration.class) { - return config.getDuration(configPropName); - } else if (parameterClass == ConfigMemorySize.class) { - return config.getMemorySize(configPropName); - } else if (parameterClass == Object.class) { - return config.getAnyRef(configPropName); - } else if (parameterClass == List.class) { - return getListValue(beanClass, parameterType, parameterClass, config, configPropName); - } else if (parameterClass == Set.class) { - return getSetValue(beanClass, parameterType, parameterClass, config, configPropName); - } else if (parameterClass == Map.class) { - // we could do better here, but right now we don't. - Type[] typeArgs = ((ParameterizedType)parameterType).getActualTypeArguments(); - if (typeArgs[0] != String.class || typeArgs[1] != Object.class) { - throw new com.gu.typesafe.config.ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported Map<" + typeArgs[0] + "," + typeArgs[1] + ">, only Map is supported right now"); - } - return config.getObject(configPropName).unwrapped(); - } else if (parameterClass == com.gu.typesafe.config.Config.class) { - return config.getConfig(configPropName); - } else if (parameterClass == com.gu.typesafe.config.ConfigObject.class) { - return config.getObject(configPropName); - } else if (parameterClass == com.gu.typesafe.config.ConfigValue.class) { - return config.getValue(configPropName); - } else if (parameterClass == com.gu.typesafe.config.ConfigList.class) { - return config.getList(configPropName); - } else if (parameterClass.isEnum()) { - @SuppressWarnings("unchecked") - Enum enumValue = config.getEnum((Class) parameterClass, configPropName); - return enumValue; - } else if (hasAtLeastOneBeanProperty(parameterClass)) { - return createInternal(config.getConfig(configPropName), parameterClass); - } else { - throw new com.gu.typesafe.config.ConfigException.BadBean("Bean property " + configPropName + " of class " + beanClass.getName() + " has unsupported type " + parameterType); - } - } - - private static Object getSetValue(Class beanClass, Type parameterType, Class parameterClass, com.gu.typesafe.config.Config config, String configPropName) { - return new HashSet((List) getListValue(beanClass, parameterType, parameterClass, config, configPropName)); - } - - private static Object getListValue(Class beanClass, Type parameterType, Class parameterClass, com.gu.typesafe.config.Config config, String configPropName) { - Type elementType = ((ParameterizedType)parameterType).getActualTypeArguments()[0]; - - if (elementType == Boolean.class) { - return config.getBooleanList(configPropName); - } else if (elementType == Integer.class) { - return config.getIntList(configPropName); - } else if (elementType == Double.class) { - return config.getDoubleList(configPropName); - } else if (elementType == Long.class) { - return config.getLongList(configPropName); - } else if (elementType == String.class) { - return config.getStringList(configPropName); - } else if (elementType == Duration.class) { - return config.getDurationList(configPropName); - } else if (elementType == ConfigMemorySize.class) { - return config.getMemorySizeList(configPropName); - } else if (elementType == Object.class) { - return config.getAnyRefList(configPropName); - } else if (elementType == com.gu.typesafe.config.Config.class) { - return config.getConfigList(configPropName); - } else if (elementType == com.gu.typesafe.config.ConfigObject.class) { - return config.getObjectList(configPropName); - } else if (elementType == ConfigValue.class) { - return config.getList(configPropName); - } else if (((Class) elementType).isEnum()) { - @SuppressWarnings("unchecked") - List enumValues = config.getEnumList((Class) elementType, configPropName); - return enumValues; - } else if (hasAtLeastOneBeanProperty((Class) elementType)) { - List beanList = new ArrayList(); - List configList = config.getConfigList(configPropName); - for (com.gu.typesafe.config.Config listMember : configList) { - beanList.add(createInternal(listMember, (Class) elementType)); - } - return beanList; - } else { - throw new ConfigException.BadBean("Bean property '" + configPropName + "' of class " + beanClass.getName() + " has unsupported list element type " + elementType); - } - } - - // null if we can't easily say; this is heuristic/best-effort - private static com.gu.typesafe.config.ConfigValueType getValueTypeOrNull(Class parameterClass) { - if (parameterClass == Boolean.class || parameterClass == boolean.class) { - return com.gu.typesafe.config.ConfigValueType.BOOLEAN; - } else if (parameterClass == Integer.class || parameterClass == int.class) { - return com.gu.typesafe.config.ConfigValueType.NUMBER; - } else if (parameterClass == Double.class || parameterClass == double.class) { - return com.gu.typesafe.config.ConfigValueType.NUMBER; - } else if (parameterClass == Long.class || parameterClass == long.class) { - return com.gu.typesafe.config.ConfigValueType.NUMBER; - } else if (parameterClass == String.class) { - return com.gu.typesafe.config.ConfigValueType.STRING; - } else if (parameterClass == Duration.class) { - return null; - } else if (parameterClass == ConfigMemorySize.class) { - return null; - } else if (parameterClass == List.class) { - return com.gu.typesafe.config.ConfigValueType.LIST; - } else if (parameterClass == Map.class) { - return com.gu.typesafe.config.ConfigValueType.OBJECT; - } else if (parameterClass == Config.class) { - return com.gu.typesafe.config.ConfigValueType.OBJECT; - } else if (parameterClass == ConfigObject.class) { - return com.gu.typesafe.config.ConfigValueType.OBJECT; - } else if (parameterClass == ConfigList.class) { - return ConfigValueType.LIST; - } else { - return null; - } - } - - private static boolean hasAtLeastOneBeanProperty(Class clazz) { - BeanInfo beanInfo = null; - try { - beanInfo = Introspector.getBeanInfo(clazz); - } catch (IntrospectionException e) { - return false; - } - - for (PropertyDescriptor beanProp : beanInfo.getPropertyDescriptors()) { - if (beanProp.getReadMethod() != null && beanProp.getWriteMethod() != null) { - return true; - } - } - - return false; - } - - private static boolean isOptionalProperty(Class beanClass, PropertyDescriptor beanProp) { - Field field = getField(beanClass, beanProp.getName()); - return field != null ? field.getAnnotationsByType(com.gu.typesafe.config.Optional.class).length > 0 : beanProp.getReadMethod().getAnnotationsByType(Optional.class).length > 0; - } - - private static Field getField(Class beanClass, String fieldName) { - try { - Field field = beanClass.getDeclaredField(fieldName); - field.setAccessible(true); - return field; - } catch (NoSuchFieldException e) { - // Don't give up yet. Try to look for field in super class, if any. - } - beanClass = beanClass.getSuperclass(); - if (beanClass == null) { - return null; - } - return getField(beanClass, fieldName); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigBoolean.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigBoolean.java deleted file mode 100644 index e85f0f29b1..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigBoolean.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.ObjectStreamException; -import java.io.Serializable; - -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigValueType; - -final class ConfigBoolean extends com.gu.typesafe.config.impl.AbstractConfigValue implements Serializable { - - private static final long serialVersionUID = 2L; - - final private boolean value; - - ConfigBoolean(com.gu.typesafe.config.ConfigOrigin origin, boolean value) { - super(origin); - this.value = value; - } - - @Override - public com.gu.typesafe.config.ConfigValueType valueType() { - return ConfigValueType.BOOLEAN; - } - - @Override - public Boolean unwrapped() { - return value; - } - - @Override - String transformToString() { - return value ? "true" : "false"; - } - - @Override - protected ConfigBoolean newCopy(ConfigOrigin origin) { - return new ConfigBoolean(origin, value); - } - - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new com.gu.typesafe.config.impl.SerializedConfigValue(this); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigConcatenation.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigConcatenation.java deleted file mode 100644 index 92fadfb295..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigConcatenation.java +++ /dev/null @@ -1,293 +0,0 @@ -package com.gu.typesafe.config.impl; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigObject; -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigValueType; -import com.gu.typesafe.config.ConfigRenderOptions; - -/** - * A ConfigConcatenation represents a list of values to be concatenated (see the - * spec). It only has to exist if at least one value is an unresolved - * substitution, otherwise we could go ahead and collapse the list into a single - * value. - * - * Right now this is always a list of strings and ${} references, but in the - * future should support a list of ConfigList. We may also support - * concatenations of objects, but ConfigDelayedMerge should be used for that - * since a concat of objects really will merge, not concatenate. - */ -final class ConfigConcatenation extends com.gu.typesafe.config.impl.AbstractConfigValue implements com.gu.typesafe.config.impl.Unmergeable, Container { - - final private List pieces; - - ConfigConcatenation(com.gu.typesafe.config.ConfigOrigin origin, List pieces) { - super(origin); - this.pieces = pieces; - - if (pieces.size() < 2) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Created concatenation with less than 2 items: " - + this); - - boolean hadUnmergeable = false; - for (com.gu.typesafe.config.impl.AbstractConfigValue p : pieces) { - if (p instanceof ConfigConcatenation) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "ConfigConcatenation should never be nested: " + this); - if (p instanceof com.gu.typesafe.config.impl.Unmergeable) - hadUnmergeable = true; - } - if (!hadUnmergeable) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "Created concatenation without an unmergeable in it: " + this); - } - - private com.gu.typesafe.config.ConfigException.NotResolved notResolved() { - return new com.gu.typesafe.config.ConfigException.NotResolved( - "need to Config#resolve(), see the API docs for Config#resolve(); substitution not resolved: " - + this); - } - - @Override - public com.gu.typesafe.config.ConfigValueType valueType() { - throw notResolved(); - } - - @Override - public Object unwrapped() { - throw notResolved(); - } - - @Override - protected ConfigConcatenation newCopy(com.gu.typesafe.config.ConfigOrigin newOrigin) { - return new ConfigConcatenation(newOrigin, pieces); - } - - @Override - protected boolean ignoresFallbacks() { - // we can never ignore fallbacks because if a child ConfigReference - // is self-referential we have to look lower in the merge stack - // for its value. - return false; - } - - @Override - public Collection unmergedValues() { - return Collections.singleton(this); - } - - private static boolean isIgnoredWhitespace(com.gu.typesafe.config.impl.AbstractConfigValue value) { - return (value instanceof ConfigString) && !((ConfigString)value).wasQuoted(); - } - - /** - * Add left and right, or their merger, to builder. - */ - private static void join(ArrayList builder, com.gu.typesafe.config.impl.AbstractConfigValue origRight) { - com.gu.typesafe.config.impl.AbstractConfigValue left = builder.get(builder.size() - 1); - com.gu.typesafe.config.impl.AbstractConfigValue right = origRight; - - // check for an object which can be converted to a list - // (this will be an object with numeric keys, like foo.0, foo.1) - if (left instanceof com.gu.typesafe.config.ConfigObject && right instanceof SimpleConfigList) { - left = DefaultTransformer.transform(left, com.gu.typesafe.config.ConfigValueType.LIST); - } else if (left instanceof SimpleConfigList && right instanceof com.gu.typesafe.config.ConfigObject) { - right = DefaultTransformer.transform(right, ConfigValueType.LIST); - } - - // Since this depends on the type of two instances, I couldn't think - // of much alternative to an instanceof chain. Visitors are sometimes - // used for multiple dispatch but seems like overkill. - com.gu.typesafe.config.impl.AbstractConfigValue joined = null; - if (left instanceof com.gu.typesafe.config.ConfigObject && right instanceof com.gu.typesafe.config.ConfigObject) { - joined = right.withFallback(left); - } else if (left instanceof SimpleConfigList && right instanceof SimpleConfigList) { - joined = ((SimpleConfigList)left).concatenate((SimpleConfigList)right); - } else if ((left instanceof SimpleConfigList || left instanceof ConfigObject) && - isIgnoredWhitespace(right)) { - joined = left; - // it should be impossible that left is whitespace and right is a list or object - } else if (left instanceof ConfigConcatenation || right instanceof ConfigConcatenation) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("unflattened ConfigConcatenation"); - } else if (left instanceof com.gu.typesafe.config.impl.Unmergeable || right instanceof com.gu.typesafe.config.impl.Unmergeable) { - // leave joined=null, cannot join - } else { - // handle primitive type or primitive type mixed with object or list - String s1 = left.transformToString(); - String s2 = right.transformToString(); - if (s1 == null || s2 == null) { - throw new com.gu.typesafe.config.ConfigException.WrongType(left.origin(), - "Cannot concatenate object or list with a non-object-or-list, " + left - + " and " + right + " are not compatible"); - } else { - com.gu.typesafe.config.ConfigOrigin joinedOrigin = SimpleConfigOrigin.mergeOrigins(left.origin(), - right.origin()); - joined = new ConfigString.Quoted(joinedOrigin, s1 + s2); - } - } - - if (joined == null) { - builder.add(right); - } else { - builder.remove(builder.size() - 1); - builder.add(joined); - } - } - - static List consolidate(List pieces) { - if (pieces.size() < 2) { - return pieces; - } else { - List flattened = new ArrayList(pieces.size()); - for (com.gu.typesafe.config.impl.AbstractConfigValue v : pieces) { - if (v instanceof ConfigConcatenation) { - flattened.addAll(((ConfigConcatenation) v).pieces); - } else { - flattened.add(v); - } - } - - ArrayList consolidated = new ArrayList( - flattened.size()); - for (com.gu.typesafe.config.impl.AbstractConfigValue v : flattened) { - if (consolidated.isEmpty()) - consolidated.add(v); - else - join(consolidated, v); - } - - return consolidated; - } - } - - static com.gu.typesafe.config.impl.AbstractConfigValue concatenate(List pieces) { - List consolidated = consolidate(pieces); - if (consolidated.isEmpty()) { - return null; - } else if (consolidated.size() == 1) { - return consolidated.get(0); - } else { - ConfigOrigin mergedOrigin = SimpleConfigOrigin.mergeOrigins(consolidated); - return new ConfigConcatenation(mergedOrigin, consolidated); - } - } - - @Override - com.gu.typesafe.config.impl.ResolveResult resolveSubstitutions(com.gu.typesafe.config.impl.ResolveContext context, ResolveSource source) - throws NotPossibleToResolve { - if (ConfigImpl.traceSubstitutionsEnabled()) { - int indent = context.depth() + 2; - ConfigImpl.trace(indent - 1, "concatenation has " + pieces.size() + " pieces:"); - int count = 0; - for (com.gu.typesafe.config.impl.AbstractConfigValue v : pieces) { - ConfigImpl.trace(indent, count + ": " + v); - count += 1; - } - } - - // Right now there's no reason to pushParent here because the - // content of ConfigConcatenation should not need to replaceChild, - // but if it did we'd have to do this. - ResolveSource sourceWithParent = source; // .pushParent(this); - com.gu.typesafe.config.impl.ResolveContext newContext = context; - - List resolved = new ArrayList(pieces.size()); - for (com.gu.typesafe.config.impl.AbstractConfigValue p : pieces) { - // to concat into a string we have to do a full resolve, - // so unrestrict the context, then put restriction back afterward - Path restriction = newContext.restrictToChild(); - com.gu.typesafe.config.impl.ResolveResult result = newContext.unrestricted() - .resolve(p, sourceWithParent); - com.gu.typesafe.config.impl.AbstractConfigValue r = result.value; - newContext = result.context.restrict(restriction); - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(context.depth(), "resolved concat piece to " + r); - if (r == null) { - // it was optional... omit - } else { - resolved.add(r); - } - } - - // now need to concat everything - List joined = consolidate(resolved); - // if unresolved is allowed we can just become another - // ConfigConcatenation - if (joined.size() > 1 && context.options().getAllowUnresolved()) - return com.gu.typesafe.config.impl.ResolveResult.make(newContext, new ConfigConcatenation(this.origin(), joined)); - else if (joined.isEmpty()) - // we had just a list of optional references using ${?} - return com.gu.typesafe.config.impl.ResolveResult.make(newContext, null); - else if (joined.size() == 1) - return com.gu.typesafe.config.impl.ResolveResult.make(newContext, joined.get(0)); - else - throw new ConfigException.BugOrBroken("Bug in the library; resolved list was joined to too many values: " - + joined); - } - - @Override - ResolveStatus resolveStatus() { - return ResolveStatus.UNRESOLVED; - } - - @Override - public ConfigConcatenation replaceChild(com.gu.typesafe.config.impl.AbstractConfigValue child, com.gu.typesafe.config.impl.AbstractConfigValue replacement) { - List newPieces = replaceChildInList(pieces, child, replacement); - if (newPieces == null) - return null; - else - return new ConfigConcatenation(origin(), newPieces); - } - - @Override - public boolean hasDescendant(com.gu.typesafe.config.impl.AbstractConfigValue descendant) { - return hasDescendantInList(pieces, descendant); - } - - // when you graft a substitution into another object, - // you have to prefix it with the location in that object - // where you grafted it; but save prefixLength so - // system property and env variable lookups don't get - // broken. - @Override - ConfigConcatenation relativized(Path prefix) { - List newPieces = new ArrayList(); - for (com.gu.typesafe.config.impl.AbstractConfigValue p : pieces) { - newPieces.add(p.relativized(prefix)); - } - return new ConfigConcatenation(origin(), newPieces); - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof ConfigConcatenation; - } - - @Override - public boolean equals(Object other) { - // note that "origin" is deliberately NOT part of equality - if (other instanceof ConfigConcatenation) { - return canEqual(other) && this.pieces.equals(((ConfigConcatenation) other).pieces); - } else { - return false; - } - } - - @Override - public int hashCode() { - // note that "origin" is deliberately NOT part of equality - return pieces.hashCode(); - } - - @Override - protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) { - for (com.gu.typesafe.config.impl.AbstractConfigValue p : pieces) { - p.render(sb, indent, atRoot, options); - } - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDelayedMerge.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDelayedMerge.java deleted file mode 100644 index fec5b6f4bd..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDelayedMerge.java +++ /dev/null @@ -1,342 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigValueType; -import com.gu.typesafe.config.ConfigRenderOptions; - -/** - * The issue here is that we want to first merge our stack of config files, and - * then we want to evaluate substitutions. But if two substitutions both expand - * to an object, we might need to merge those two objects. Thus, we can't ever - * "override" a substitution when we do a merge; instead we have to save the - * stack of values that should be merged, and resolve the merge when we evaluate - * substitutions. - */ -final class ConfigDelayedMerge extends com.gu.typesafe.config.impl.AbstractConfigValue implements com.gu.typesafe.config.impl.Unmergeable, - com.gu.typesafe.config.impl.ReplaceableMergeStack { - - // earlier items in the stack win - final private List stack; - - ConfigDelayedMerge(com.gu.typesafe.config.ConfigOrigin origin, List stack) { - super(origin); - this.stack = stack; - if (stack.isEmpty()) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "creating empty delayed merge value"); - - for (com.gu.typesafe.config.impl.AbstractConfigValue v : stack) { - if (v instanceof ConfigDelayedMerge || v instanceof com.gu.typesafe.config.impl.ConfigDelayedMergeObject) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "placed nested DelayedMerge in a ConfigDelayedMerge, should have consolidated stack"); - } - } - - @Override - public ConfigValueType valueType() { - throw new com.gu.typesafe.config.ConfigException.NotResolved( - "called valueType() on value with unresolved substitutions, need to Config#resolve() first, see API docs"); - } - - @Override - public Object unwrapped() { - throw new com.gu.typesafe.config.ConfigException.NotResolved( - "called unwrapped() on value with unresolved substitutions, need to Config#resolve() first, see API docs"); - } - - @Override - com.gu.typesafe.config.impl.ResolveResult resolveSubstitutions(com.gu.typesafe.config.impl.ResolveContext context, com.gu.typesafe.config.impl.ResolveSource source) - throws NotPossibleToResolve { - return resolveSubstitutions(this, stack, context, source); - } - - // static method also used by ConfigDelayedMergeObject - static com.gu.typesafe.config.impl.ResolveResult resolveSubstitutions(com.gu.typesafe.config.impl.ReplaceableMergeStack replaceable, - List stack, - com.gu.typesafe.config.impl.ResolveContext context, com.gu.typesafe.config.impl.ResolveSource source) throws NotPossibleToResolve { - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) { - com.gu.typesafe.config.impl.ConfigImpl.trace(context.depth(), "delayed merge stack has " + stack.size() + " items:"); - int count = 0; - for (com.gu.typesafe.config.impl.AbstractConfigValue v : stack) { - com.gu.typesafe.config.impl.ConfigImpl.trace(context.depth() + 1, count + ": " + v); - count += 1; - } - } - - // to resolve substitutions, we need to recursively resolve - // the stack of stuff to merge, and merge the stack so - // we won't be a delayed merge anymore. If restrictToChildOrNull - // is non-null, or resolve options allow partial resolves, - // we may remain a delayed merge though. - - com.gu.typesafe.config.impl.ResolveContext newContext = context; - int count = 0; - com.gu.typesafe.config.impl.AbstractConfigValue merged = null; - for (com.gu.typesafe.config.impl.AbstractConfigValue end : stack) { - // the end value may or may not be resolved already - - com.gu.typesafe.config.impl.ResolveSource sourceForEnd; - - if (end instanceof com.gu.typesafe.config.impl.ReplaceableMergeStack) - throw new ConfigException.BugOrBroken("A delayed merge should not contain another one: " + replaceable); - else if (end instanceof com.gu.typesafe.config.impl.Unmergeable) { - // the remainder could be any kind of value, including another - // ConfigDelayedMerge - com.gu.typesafe.config.impl.AbstractConfigValue remainder = replaceable.makeReplacement(context, count + 1); - - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - com.gu.typesafe.config.impl.ConfigImpl.trace(newContext.depth(), "remainder portion: " + remainder); - - // If, while resolving 'end' we come back to the same - // merge stack, we only want to look _below_ 'end' - // in the stack. So we arrange to replace the - // ConfigDelayedMerge with a value that is only - // the remainder of the stack below this one. - - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - com.gu.typesafe.config.impl.ConfigImpl.trace(newContext.depth(), "building sourceForEnd"); - - // we resetParents() here because we'll be resolving "end" - // against a root which does NOT contain "end" - sourceForEnd = source.replaceWithinCurrentParent((com.gu.typesafe.config.impl.AbstractConfigValue) replaceable, remainder); - - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - com.gu.typesafe.config.impl.ConfigImpl.trace(newContext.depth(), " sourceForEnd before reset parents but after replace: " - + sourceForEnd); - - sourceForEnd = sourceForEnd.resetParents(); - } else { - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - com.gu.typesafe.config.impl.ConfigImpl.trace(newContext.depth(), - "will resolve end against the original source with parent pushed"); - - sourceForEnd = source.pushParent(replaceable); - } - - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) { - com.gu.typesafe.config.impl.ConfigImpl.trace(newContext.depth(), "sourceForEnd =" + sourceForEnd); - } - - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - com.gu.typesafe.config.impl.ConfigImpl.trace(newContext.depth(), "Resolving highest-priority item in delayed merge " + end - + " against " + sourceForEnd + " endWasRemoved=" + (source != sourceForEnd)); - com.gu.typesafe.config.impl.ResolveResult result = newContext.resolve(end, sourceForEnd); - com.gu.typesafe.config.impl.AbstractConfigValue resolvedEnd = result.value; - newContext = result.context; - - if (resolvedEnd != null) { - if (merged == null) { - merged = resolvedEnd; - } else { - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - com.gu.typesafe.config.impl.ConfigImpl.trace(newContext.depth() + 1, "merging " + merged + " with fallback " + resolvedEnd); - merged = merged.withFallback(resolvedEnd); - } - } - - count += 1; - - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - com.gu.typesafe.config.impl.ConfigImpl.trace(newContext.depth(), "stack merged, yielding: " + merged); - } - - return com.gu.typesafe.config.impl.ResolveResult.make(newContext, merged); - } - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue makeReplacement(com.gu.typesafe.config.impl.ResolveContext context, int skipping) { - return ConfigDelayedMerge.makeReplacement(context, stack, skipping); - } - - // static method also used by ConfigDelayedMergeObject; end may be null - static com.gu.typesafe.config.impl.AbstractConfigValue makeReplacement(com.gu.typesafe.config.impl.ResolveContext context, List stack, int skipping) { - List subStack = stack.subList(skipping, stack.size()); - - if (subStack.isEmpty()) { - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(context.depth(), "Nothing else in the merge stack, replacing with null"); - return null; - } else { - // generate a new merge stack from only the remaining items - com.gu.typesafe.config.impl.AbstractConfigValue merged = null; - for (com.gu.typesafe.config.impl.AbstractConfigValue v : subStack) { - if (merged == null) - merged = v; - else - merged = merged.withFallback(v); - } - return merged; - } - } - - @Override - ResolveStatus resolveStatus() { - return ResolveStatus.UNRESOLVED; - } - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue replaceChild(com.gu.typesafe.config.impl.AbstractConfigValue child, com.gu.typesafe.config.impl.AbstractConfigValue replacement) { - List newStack = replaceChildInList(stack, child, replacement); - if (newStack == null) - return null; - else - return new ConfigDelayedMerge(origin(), newStack); - } - - @Override - public boolean hasDescendant(com.gu.typesafe.config.impl.AbstractConfigValue descendant) { - return hasDescendantInList(stack, descendant); - } - - @Override - ConfigDelayedMerge relativized(Path prefix) { - List newStack = new ArrayList(); - for (com.gu.typesafe.config.impl.AbstractConfigValue o : stack) { - newStack.add(o.relativized(prefix)); - } - return new ConfigDelayedMerge(origin(), newStack); - } - - // static utility shared with ConfigDelayedMergeObject - static boolean stackIgnoresFallbacks(List stack) { - com.gu.typesafe.config.impl.AbstractConfigValue last = stack.get(stack.size() - 1); - return last.ignoresFallbacks(); - } - - @Override - protected boolean ignoresFallbacks() { - return stackIgnoresFallbacks(stack); - } - - @Override - protected com.gu.typesafe.config.impl.AbstractConfigValue newCopy(ConfigOrigin newOrigin) { - return new ConfigDelayedMerge(newOrigin, stack); - } - - @Override - protected final ConfigDelayedMerge mergedWithTheUnmergeable(com.gu.typesafe.config.impl.Unmergeable fallback) { - return (ConfigDelayedMerge) mergedWithTheUnmergeable(stack, fallback); - } - - @Override - protected final ConfigDelayedMerge mergedWithObject(AbstractConfigObject fallback) { - return (ConfigDelayedMerge) mergedWithObject(stack, fallback); - } - - @Override - protected ConfigDelayedMerge mergedWithNonObject(com.gu.typesafe.config.impl.AbstractConfigValue fallback) { - return (ConfigDelayedMerge) mergedWithNonObject(stack, fallback); - } - - @Override - public Collection unmergedValues() { - return stack; - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof ConfigDelayedMerge; - } - - @Override - public boolean equals(Object other) { - // note that "origin" is deliberately NOT part of equality - if (other instanceof ConfigDelayedMerge) { - return canEqual(other) - && (this.stack == ((ConfigDelayedMerge) other).stack || this.stack - .equals(((ConfigDelayedMerge) other).stack)); - } else { - return false; - } - } - - @Override - public int hashCode() { - // note that "origin" is deliberately NOT part of equality - return stack.hashCode(); - } - - @Override - protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, ConfigRenderOptions options) { - render(stack, sb, indent, atRoot, atKey, options); - } - - @Override - protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) { - render(sb, indent, atRoot, null, options); - } - - // static method also used by ConfigDelayedMergeObject. - static void render(List stack, StringBuilder sb, int indent, boolean atRoot, String atKey, - ConfigRenderOptions options) { - boolean commentMerge = options.getComments(); - if (commentMerge) { - sb.append("# unresolved merge of " + stack.size() + " values follows (\n"); - if (atKey == null) { - indent(sb, indent, options); - sb.append("# this unresolved merge will not be parseable because it's at the root of the object\n"); - indent(sb, indent, options); - sb.append("# the HOCON format has no way to list multiple root objects in a single file\n"); - } - } - - List reversed = new ArrayList(); - reversed.addAll(stack); - Collections.reverse(reversed); - - int i = 0; - for (com.gu.typesafe.config.impl.AbstractConfigValue v : reversed) { - if (commentMerge) { - indent(sb, indent, options); - if (atKey != null) { - sb.append("# unmerged value " + i + " for key " - + com.gu.typesafe.config.impl.ConfigImplUtil.renderJsonString(atKey) + " from "); - } else { - sb.append("# unmerged value " + i + " from "); - } - i += 1; - sb.append(v.origin().description()); - sb.append("\n"); - - for (String comment : v.origin().comments()) { - indent(sb, indent, options); - sb.append("# "); - sb.append(comment); - sb.append("\n"); - } - } - indent(sb, indent, options); - - if (atKey != null) { - sb.append(ConfigImplUtil.renderJsonString(atKey)); - if (options.getFormatted()) - sb.append(" : "); - else - sb.append(":"); - } - v.render(sb, indent, atRoot, options); - sb.append(","); - if (options.getFormatted()) - sb.append('\n'); - } - // chop comma or newline - sb.setLength(sb.length() - 1); - if (options.getFormatted()) { - sb.setLength(sb.length() - 1); // also chop comma - sb.append("\n"); // put a newline back - } - if (commentMerge) { - indent(sb, indent, options); - sb.append("# ) end of unresolved merge\n"); - } - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDelayedMergeObject.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDelayedMergeObject.java deleted file mode 100644 index b458ccf208..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDelayedMergeObject.java +++ /dev/null @@ -1,326 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigList; -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigValue; -import com.gu.typesafe.config.ConfigMergeable; -import com.gu.typesafe.config.ConfigRenderOptions; - -// This is just like ConfigDelayedMerge except we know statically -// that it will turn out to be an object. -final class ConfigDelayedMergeObject extends AbstractConfigObject implements com.gu.typesafe.config.impl.Unmergeable, - com.gu.typesafe.config.impl.ReplaceableMergeStack { - - final private List stack; - - ConfigDelayedMergeObject(com.gu.typesafe.config.ConfigOrigin origin, List stack) { - super(origin); - this.stack = stack; - - if (stack.isEmpty()) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "creating empty delayed merge object"); - if (!(stack.get(0) instanceof AbstractConfigObject)) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "created a delayed merge object not guaranteed to be an object"); - - for (com.gu.typesafe.config.impl.AbstractConfigValue v : stack) { - if (v instanceof ConfigDelayedMerge || v instanceof ConfigDelayedMergeObject) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "placed nested DelayedMerge in a ConfigDelayedMergeObject, should have consolidated stack"); - } - } - - @Override - protected ConfigDelayedMergeObject newCopy(ResolveStatus status, ConfigOrigin origin) { - if (status != resolveStatus()) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "attempt to create resolved ConfigDelayedMergeObject"); - return new ConfigDelayedMergeObject(origin, stack); - } - - @Override - com.gu.typesafe.config.impl.ResolveResult resolveSubstitutions(com.gu.typesafe.config.impl.ResolveContext context, com.gu.typesafe.config.impl.ResolveSource source) - throws NotPossibleToResolve { - com.gu.typesafe.config.impl.ResolveResult merged = ConfigDelayedMerge.resolveSubstitutions(this, stack, - context, source); - return merged.asObjectResult(); - } - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue makeReplacement(com.gu.typesafe.config.impl.ResolveContext context, int skipping) { - return ConfigDelayedMerge.makeReplacement(context, stack, skipping); - } - - @Override - ResolveStatus resolveStatus() { - return ResolveStatus.UNRESOLVED; - } - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue replaceChild(com.gu.typesafe.config.impl.AbstractConfigValue child, com.gu.typesafe.config.impl.AbstractConfigValue replacement) { - List newStack = replaceChildInList(stack, child, replacement); - if (newStack == null) - return null; - else - return new ConfigDelayedMergeObject(origin(), newStack); - } - - @Override - public boolean hasDescendant(com.gu.typesafe.config.impl.AbstractConfigValue descendant) { - return hasDescendantInList(stack, descendant); - } - - @Override - ConfigDelayedMergeObject relativized(Path prefix) { - List newStack = new ArrayList(); - for (com.gu.typesafe.config.impl.AbstractConfigValue o : stack) { - newStack.add(o.relativized(prefix)); - } - return new ConfigDelayedMergeObject(origin(), newStack); - } - - @Override - protected boolean ignoresFallbacks() { - return ConfigDelayedMerge.stackIgnoresFallbacks(stack); - } - - @Override - protected final ConfigDelayedMergeObject mergedWithTheUnmergeable(com.gu.typesafe.config.impl.Unmergeable fallback) { - requireNotIgnoringFallbacks(); - - return (ConfigDelayedMergeObject) mergedWithTheUnmergeable(stack, fallback); - } - - @Override - protected final ConfigDelayedMergeObject mergedWithObject(AbstractConfigObject fallback) { - return mergedWithNonObject(fallback); - } - - @Override - protected final ConfigDelayedMergeObject mergedWithNonObject(com.gu.typesafe.config.impl.AbstractConfigValue fallback) { - requireNotIgnoringFallbacks(); - - return (ConfigDelayedMergeObject) mergedWithNonObject(stack, fallback); - } - - @Override - public ConfigDelayedMergeObject withFallback(ConfigMergeable mergeable) { - return (ConfigDelayedMergeObject) super.withFallback(mergeable); - } - - @Override - public ConfigDelayedMergeObject withOnlyKey(String key) { - throw notResolved(); - } - - @Override - public ConfigDelayedMergeObject withoutKey(String key) { - throw notResolved(); - } - - @Override - protected AbstractConfigObject withOnlyPathOrNull(Path path) { - throw notResolved(); - } - - @Override - AbstractConfigObject withOnlyPath(Path path) { - throw notResolved(); - } - - @Override - AbstractConfigObject withoutPath(Path path) { - throw notResolved(); - } - - @Override - public ConfigDelayedMergeObject withValue(String key, com.gu.typesafe.config.ConfigValue value) { - throw notResolved(); - } - - @Override - ConfigDelayedMergeObject withValue(Path path, com.gu.typesafe.config.ConfigValue value) { - throw notResolved(); - } - - @Override - public Collection unmergedValues() { - return stack; - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof ConfigDelayedMergeObject; - } - - @Override - public boolean equals(Object other) { - // note that "origin" is deliberately NOT part of equality - if (other instanceof ConfigDelayedMergeObject) { - return canEqual(other) - && (this.stack == ((ConfigDelayedMergeObject) other).stack || this.stack - .equals(((ConfigDelayedMergeObject) other).stack)); - } else { - return false; - } - } - - @Override - public int hashCode() { - // note that "origin" is deliberately NOT part of equality - return stack.hashCode(); - } - - @Override - protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey, ConfigRenderOptions options) { - ConfigDelayedMerge.render(stack, sb, indent, atRoot, atKey, options); - } - - @Override - protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) { - render(sb, indent, atRoot, null, options); - } - - private static com.gu.typesafe.config.ConfigException notResolved() { - return new com.gu.typesafe.config.ConfigException.NotResolved( - "need to Config#resolve() before using this object, see the API docs for Config#resolve()"); - } - - @Override - public Map unwrapped() { - throw notResolved(); - } - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue get(Object key) { - throw notResolved(); - } - - @Override - public boolean containsKey(Object key) { - throw notResolved(); - } - - @Override - public boolean containsValue(Object value) { - throw notResolved(); - } - - @Override - public Set> entrySet() { - throw notResolved(); - } - - @Override - public boolean isEmpty() { - throw notResolved(); - } - - @Override - public Set keySet() { - throw notResolved(); - } - - @Override - public int size() { - throw notResolved(); - } - - @Override - public Collection values() { - throw notResolved(); - } - - @Override - protected com.gu.typesafe.config.impl.AbstractConfigValue attemptPeekWithPartialResolve(String key) { - // a partial resolve of a ConfigDelayedMergeObject always results in a - // SimpleConfigObject because all the substitutions in the stack get - // resolved in order to look up the partial. - // So we know here that we have not been resolved at all even - // partially. - // Given that, all this code is probably gratuitous, since the app code - // is likely broken. But in general we only throw NotResolved if you try - // to touch the exact key that isn't resolved, so this is in that - // spirit. - - // we'll be able to return a key if we have a value that ignores - // fallbacks, prior to any unmergeable values. - for (com.gu.typesafe.config.impl.AbstractConfigValue layer : stack) { - if (layer instanceof AbstractConfigObject) { - AbstractConfigObject objectLayer = (AbstractConfigObject) layer; - com.gu.typesafe.config.impl.AbstractConfigValue v = objectLayer.attemptPeekWithPartialResolve(key); - if (v != null) { - if (v.ignoresFallbacks()) { - // we know we won't need to merge anything in to this - // value - return v; - } else { - // we can't return this value because we know there are - // unmergeable values later in the stack that may - // contain values that need to be merged with this - // value. we'll throw the exception when we get to those - // unmergeable values, so continue here. - continue; - } - } else if (layer instanceof com.gu.typesafe.config.impl.Unmergeable) { - // an unmergeable object (which would be another - // ConfigDelayedMergeObject) can't know that a key is - // missing, so it can't return null; it can only return a - // value or throw NotPossibleToResolve - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "should not be reached: unmergeable object returned null value"); - } else { - // a non-unmergeable AbstractConfigObject that returned null - // for the key in question is not relevant, we can keep - // looking for a value. - continue; - } - } else if (layer instanceof com.gu.typesafe.config.impl.Unmergeable) { - throw new com.gu.typesafe.config.ConfigException.NotResolved("Key '" + key + "' is not available at '" - + origin().description() + "' because value at '" - + layer.origin().description() - + "' has not been resolved and may turn out to contain or hide '" + key - + "'." - + " Be sure to Config#resolve() before using a config object."); - } else if (layer.resolveStatus() == ResolveStatus.UNRESOLVED) { - // if the layer is not an object, and not a substitution or - // merge, - // then it's something that's unresolved because it _contains_ - // an unresolved object... i.e. it's an array - if (!(layer instanceof ConfigList)) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Expecting a list here, not " + layer); - // all later objects will be hidden so we can say we won't find - // the key - return null; - } else { - // non-object, but resolved, like an integer or something. - // has no children so the one we're after won't be in it. - // we would only have this in the stack in case something - // else "looks back" to it due to a cycle. - // anyway at this point we know we can't find the key anymore. - if (!layer.ignoresFallbacks()) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "resolved non-object should ignore fallbacks"); - } - return null; - } - } - // If we get here, then we never found anything unresolved which means - // the ConfigDelayedMergeObject should not have existed. some - // invariant was violated. - throw new ConfigException.BugOrBroken( - "Delayed merge stack does not contain any unmergeable values"); - - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDocumentParser.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDocumentParser.java deleted file mode 100644 index c25336ddb0..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDocumentParser.java +++ /dev/null @@ -1,712 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.*; - -import com.gu.typesafe.config.ConfigSyntax; - -final class ConfigDocumentParser { - static com.gu.typesafe.config.impl.ConfigNodeRoot parse(Iterator tokens, com.gu.typesafe.config.ConfigOrigin origin, com.gu.typesafe.config.ConfigParseOptions options) { - com.gu.typesafe.config.ConfigSyntax syntax = options.getSyntax() == null ? com.gu.typesafe.config.ConfigSyntax.CONF : options.getSyntax(); - ParseContext context = new ParseContext(syntax, origin, tokens); - return context.parse(); - } - - static com.gu.typesafe.config.impl.AbstractConfigNodeValue parseValue(Iterator tokens, com.gu.typesafe.config.ConfigOrigin origin, com.gu.typesafe.config.ConfigParseOptions options) { - com.gu.typesafe.config.ConfigSyntax syntax = options.getSyntax() == null ? com.gu.typesafe.config.ConfigSyntax.CONF : options.getSyntax(); - ParseContext context = new ParseContext(syntax, origin, tokens); - return context.parseSingleValue(); - } - - static private final class ParseContext { - private int lineNumber; - final private Stack buffer; - final private Iterator tokens; - final private com.gu.typesafe.config.ConfigSyntax flavor; - final private com.gu.typesafe.config.ConfigOrigin baseOrigin; - // this is the number of "equals" we are inside, - // used to modify the error message to reflect that - // someone may think this is .properties format. - int equalsCount; - - ParseContext(com.gu.typesafe.config.ConfigSyntax flavor, com.gu.typesafe.config.ConfigOrigin origin, Iterator tokens) { - lineNumber = 1; - buffer = new Stack(); - this.tokens = tokens; - this.flavor = flavor; - this.equalsCount = 0; - this.baseOrigin = origin; - } - - private com.gu.typesafe.config.impl.Token popToken() { - if (buffer.isEmpty()) { - return tokens.next(); - } - return buffer.pop(); - } - - private com.gu.typesafe.config.impl.Token nextToken() { - com.gu.typesafe.config.impl.Token t = popToken(); - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) { - if (com.gu.typesafe.config.impl.Tokens.isUnquotedText(t) && !isUnquotedWhitespace(t)) { - throw parseError("Token not allowed in valid JSON: '" - + com.gu.typesafe.config.impl.Tokens.getUnquotedText(t) + "'"); - } else if (com.gu.typesafe.config.impl.Tokens.isSubstitution(t)) { - throw parseError("Substitutions (${} syntax) not allowed in JSON"); - } - } - return t; - } - - private com.gu.typesafe.config.impl.Token nextTokenCollectingWhitespace(Collection nodes) { - while (true) { - com.gu.typesafe.config.impl.Token t = nextToken(); - if (com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(t) || com.gu.typesafe.config.impl.Tokens.isNewline(t) || isUnquotedWhitespace(t)) { - nodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - if (com.gu.typesafe.config.impl.Tokens.isNewline(t)) { - lineNumber = t.lineNumber() + 1; - } - } else if (com.gu.typesafe.config.impl.Tokens.isComment(t)) { - nodes.add(new com.gu.typesafe.config.impl.ConfigNodeComment(t)); - } else { - int newNumber = t.lineNumber(); - if (newNumber >= 0) - lineNumber = newNumber; - return t; - } - } - } - - private void putBack(com.gu.typesafe.config.impl.Token token) { - buffer.push(token); - } - - // In arrays and objects, comma can be omitted - // as long as there's at least one newline instead. - // this skips any newlines in front of a comma, - // skips the comma, and returns true if it found - // either a newline or a comma. The iterator - // is left just after the comma or the newline. - private boolean checkElementSeparator(Collection nodes) { - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) { - com.gu.typesafe.config.impl.Token t = nextTokenCollectingWhitespace(nodes); - if (t == com.gu.typesafe.config.impl.Tokens.COMMA) { - nodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - return true; - } else { - putBack(t); - return false; - } - } else { - boolean sawSeparatorOrNewline = false; - com.gu.typesafe.config.impl.Token t = nextToken(); - while (true) { - if (com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(t) || isUnquotedWhitespace(t)) { - nodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - } else if (com.gu.typesafe.config.impl.Tokens.isComment(t)) { - nodes.add(new com.gu.typesafe.config.impl.ConfigNodeComment(t)); - } else if (com.gu.typesafe.config.impl.Tokens.isNewline(t)) { - sawSeparatorOrNewline = true; - lineNumber++; - nodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - // we want to continue to also eat - // a comma if there is one. - } else if (t == com.gu.typesafe.config.impl.Tokens.COMMA) { - nodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - return true; - } else { - // non-newline-or-comma - putBack(t); - return sawSeparatorOrNewline; - } - t = nextToken(); - } - } - } - - // parse a concatenation. If there is no concatenation, return the next value - private com.gu.typesafe.config.impl.AbstractConfigNodeValue consolidateValues(Collection nodes) { - // this trick is not done in JSON - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) - return null; - - // create only if we have value tokens - ArrayList values = new ArrayList(); - int valueCount = 0; - - // ignore a newline up front - com.gu.typesafe.config.impl.Token t = nextTokenCollectingWhitespace(nodes); - while (true) { - com.gu.typesafe.config.impl.AbstractConfigNodeValue v = null; - if (com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(t)) { - values.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - t = nextToken(); - continue; - } - else if (com.gu.typesafe.config.impl.Tokens.isValue(t) || com.gu.typesafe.config.impl.Tokens.isUnquotedText(t) - || com.gu.typesafe.config.impl.Tokens.isSubstitution(t) || t == com.gu.typesafe.config.impl.Tokens.OPEN_CURLY - || t == com.gu.typesafe.config.impl.Tokens.OPEN_SQUARE) { - // there may be newlines _within_ the objects and arrays - v = parseValue(t); - valueCount++; - } else { - break; - } - - if (v == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("no value"); - - values.add(v); - - t = nextToken(); // but don't consolidate across a newline - } - - putBack(t); - - // No concatenation was seen, but a single value may have been parsed, so return it, and put back - // all succeeding tokens - if (valueCount < 2) { - com.gu.typesafe.config.impl.AbstractConfigNodeValue value = null; - for (com.gu.typesafe.config.impl.AbstractConfigNode node : values) { - if (node instanceof com.gu.typesafe.config.impl.AbstractConfigNodeValue) - value = (com.gu.typesafe.config.impl.AbstractConfigNodeValue)node; - else if (value == null) - nodes.add(node); - else - putBack((new ArrayList(node.tokens())).get(0)); - } - return value; - } - - // Put back any trailing whitespace, as the parent object is responsible for tracking - // any leading/trailing whitespace - for (int i = values.size() - 1; i >= 0; i--) { - if (values.get(i) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken) { - putBack(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) values.get(i)).token()); - values.remove(i); - } else { - break; - } - } - return new com.gu.typesafe.config.impl.ConfigNodeConcatenation(values); - } - - private com.gu.typesafe.config.ConfigException parseError(String message) { - return parseError(message, null); - } - - private com.gu.typesafe.config.ConfigException parseError(String message, Throwable cause) { - return new com.gu.typesafe.config.ConfigException.Parse(baseOrigin.withLineNumber(lineNumber), message, cause); - } - - private String addQuoteSuggestion(String badToken, String message) { - return addQuoteSuggestion(null, equalsCount > 0, badToken, message); - } - - private String addQuoteSuggestion(Path lastPath, boolean insideEquals, String badToken, - String message) { - String previousFieldName = lastPath != null ? lastPath.render() : null; - - String part; - if (badToken.equals(com.gu.typesafe.config.impl.Tokens.END.toString())) { - // EOF requires special handling for the error to make sense. - if (previousFieldName != null) - part = message + " (if you intended '" + previousFieldName - + "' to be part of a value, instead of a key, " - + "try adding double quotes around the whole value"; - else - return message; - } else { - if (previousFieldName != null) { - part = message + " (if you intended " + badToken - + " to be part of the value for '" + previousFieldName + "', " - + "try enclosing the value in double quotes"; - } else { - part = message + " (if you intended " + badToken - + " to be part of a key or string value, " - + "try enclosing the key or value in double quotes"; - } - } - - if (insideEquals) - return part - + ", or you may be able to rename the file .properties rather than .conf)"; - else - return part + ")"; - } - - private com.gu.typesafe.config.impl.AbstractConfigNodeValue parseValue(com.gu.typesafe.config.impl.Token t) { - com.gu.typesafe.config.impl.AbstractConfigNodeValue v = null; - int startingEqualsCount = equalsCount; - - if (com.gu.typesafe.config.impl.Tokens.isValue(t) || com.gu.typesafe.config.impl.Tokens.isUnquotedText(t) || com.gu.typesafe.config.impl.Tokens.isSubstitution(t)) { - v = new com.gu.typesafe.config.impl.ConfigNodeSimpleValue(t); - } else if (t == com.gu.typesafe.config.impl.Tokens.OPEN_CURLY) { - v = parseObject(true); - } else if (t== com.gu.typesafe.config.impl.Tokens.OPEN_SQUARE) { - v = parseArray(); - } else { - throw parseError(addQuoteSuggestion(t.toString(), - "Expecting a value but got wrong token: " + t)); - } - - if (equalsCount != startingEqualsCount) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Bug in config parser: unbalanced equals count"); - - return v; - } - - private com.gu.typesafe.config.impl.ConfigNodePath parseKey(com.gu.typesafe.config.impl.Token token) { - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) { - if (com.gu.typesafe.config.impl.Tokens.isValueWithType(token, com.gu.typesafe.config.ConfigValueType.STRING)) { - return com.gu.typesafe.config.impl.PathParser.parsePathNodeExpression(Collections.singletonList(token).iterator(), - baseOrigin.withLineNumber(lineNumber)); - } else { - throw parseError("Expecting close brace } or a field name here, got " - + token); - } - } else { - List expression = new ArrayList(); - com.gu.typesafe.config.impl.Token t = token; - while (com.gu.typesafe.config.impl.Tokens.isValue(t) || com.gu.typesafe.config.impl.Tokens.isUnquotedText(t)) { - expression.add(t); - t = nextToken(); // note: don't cross a newline - } - - if (expression.isEmpty()) { - throw parseError(ExpectingClosingParenthesisError + t); - } - - putBack(t); // put back the token we ended with - return com.gu.typesafe.config.impl.PathParser.parsePathNodeExpression(expression.iterator(), - baseOrigin.withLineNumber(lineNumber)); - } - } - - private static boolean isIncludeKeyword(com.gu.typesafe.config.impl.Token t) { - return com.gu.typesafe.config.impl.Tokens.isUnquotedText(t) - && com.gu.typesafe.config.impl.Tokens.getUnquotedText(t).equals("include"); - } - - private static boolean isUnquotedWhitespace(com.gu.typesafe.config.impl.Token t) { - if (!com.gu.typesafe.config.impl.Tokens.isUnquotedText(t)) - return false; - - String s = com.gu.typesafe.config.impl.Tokens.getUnquotedText(t); - - for (int i = 0; i < s.length(); ++i) { - char c = s.charAt(i); - if (!ConfigImplUtil.isWhitespace(c)) - return false; - } - return true; - } - - private boolean isKeyValueSeparatorToken(com.gu.typesafe.config.impl.Token t) { - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) { - return t == com.gu.typesafe.config.impl.Tokens.COLON; - } else { - return t == com.gu.typesafe.config.impl.Tokens.COLON || t == com.gu.typesafe.config.impl.Tokens.EQUALS || t == com.gu.typesafe.config.impl.Tokens.PLUS_EQUALS; - } - } - - private final String ExpectingClosingParenthesisError = "expecting a close parentheses ')' here, not: "; - - private com.gu.typesafe.config.impl.ConfigNodeInclude parseInclude(ArrayList children) { - - com.gu.typesafe.config.impl.Token t = nextTokenCollectingWhitespace(children); - - // we either have a 'required()' or a quoted string or the "file()" syntax - if (com.gu.typesafe.config.impl.Tokens.isUnquotedText(t)) { - String kindText = com.gu.typesafe.config.impl.Tokens.getUnquotedText(t); - - if (kindText.startsWith("required(")) { - String r = kindText.replaceFirst("required\\(",""); - if (r.length()>0) { - putBack(com.gu.typesafe.config.impl.Tokens.newUnquotedText(t.origin(),r)); - } - - children.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - //children.add(new ConfigNodeSingleToken(tOpen)); - - com.gu.typesafe.config.impl.ConfigNodeInclude res = parseIncludeResource(children, true); - - t = nextTokenCollectingWhitespace(children); - - if (com.gu.typesafe.config.impl.Tokens.isUnquotedText(t) && com.gu.typesafe.config.impl.Tokens.getUnquotedText(t).equals(")")) { - // OK, close paren - } else { - throw parseError(ExpectingClosingParenthesisError + t); - } - - return res; - } else { - putBack(t); - return parseIncludeResource(children, false); - } - } - else { - putBack(t); - return parseIncludeResource(children, false); - } - } - - private com.gu.typesafe.config.impl.ConfigNodeInclude parseIncludeResource(ArrayList children, boolean isRequired) { - com.gu.typesafe.config.impl.Token t = nextTokenCollectingWhitespace(children); - - // we either have a quoted string or the "file()" syntax - if (com.gu.typesafe.config.impl.Tokens.isUnquotedText(t)) { - // get foo( - String kindText = com.gu.typesafe.config.impl.Tokens.getUnquotedText(t); - com.gu.typesafe.config.impl.ConfigIncludeKind kind; - String prefix; - - if (kindText.startsWith("url(")) { - kind = com.gu.typesafe.config.impl.ConfigIncludeKind.URL; - prefix = "url("; - } else if (kindText.startsWith("file(")) { - kind = com.gu.typesafe.config.impl.ConfigIncludeKind.FILE; - prefix = "file("; - } else if (kindText.startsWith("classpath(")) { - kind = com.gu.typesafe.config.impl.ConfigIncludeKind.CLASSPATH; - prefix = "classpath("; - } else { - throw parseError("expecting include parameter to be quoted filename, file(), classpath(), or url(). No spaces are allowed before the open paren. Not expecting: " - + t); - } - String r = kindText.replaceFirst("[^(]*\\(",""); - if (r.length()>0) { - putBack(com.gu.typesafe.config.impl.Tokens.newUnquotedText(t.origin(),r)); - } - - children.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - - // skip space inside parens - t = nextTokenCollectingWhitespace(children); - - // quoted string - if (!com.gu.typesafe.config.impl.Tokens.isValueWithType(t, com.gu.typesafe.config.ConfigValueType.STRING)) { - throw parseError("expecting include " + prefix + ") parameter to be a quoted string, rather than: " + t); - } - children.add(new com.gu.typesafe.config.impl.ConfigNodeSimpleValue(t)); - // skip space after string, inside parens - t = nextTokenCollectingWhitespace(children); - - if (com.gu.typesafe.config.impl.Tokens.isUnquotedText(t) && com.gu.typesafe.config.impl.Tokens.getUnquotedText(t).startsWith(")")) { - String rest = com.gu.typesafe.config.impl.Tokens.getUnquotedText(t).substring(1); - if (rest.length()>0) { - putBack(com.gu.typesafe.config.impl.Tokens.newUnquotedText(t.origin(),rest)); - } - // OK, close paren - } else { - throw parseError(ExpectingClosingParenthesisError + t); - } - - return new com.gu.typesafe.config.impl.ConfigNodeInclude(children, kind, isRequired); - } else if (com.gu.typesafe.config.impl.Tokens.isValueWithType(t, com.gu.typesafe.config.ConfigValueType.STRING)) { - children.add(new com.gu.typesafe.config.impl.ConfigNodeSimpleValue(t)); - return new com.gu.typesafe.config.impl.ConfigNodeInclude(children, com.gu.typesafe.config.impl.ConfigIncludeKind.HEURISTIC, isRequired); - } else { - throw parseError("include keyword is not followed by a quoted string, but by: " + t); - } - } - - private com.gu.typesafe.config.impl.ConfigNodeComplexValue parseObject(boolean hadOpenCurly) { - // invoked just after the OPEN_CURLY (or START, if !hadOpenCurly) - boolean afterComma = false; - Path lastPath = null; - boolean lastInsideEquals = false; - ArrayList objectNodes = new ArrayList(); - ArrayList keyValueNodes; - HashMap keys = new HashMap(); - if (hadOpenCurly) - objectNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.OPEN_CURLY)); - - while (true) { - com.gu.typesafe.config.impl.Token t = nextTokenCollectingWhitespace(objectNodes); - if (t == com.gu.typesafe.config.impl.Tokens.CLOSE_CURLY) { - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON && afterComma) { - throw parseError(addQuoteSuggestion(t.toString(), - "expecting a field name after a comma, got a close brace } instead")); - } else if (!hadOpenCurly) { - throw parseError(addQuoteSuggestion(t.toString(), - "unbalanced close brace '}' with no open brace")); - } - objectNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.CLOSE_CURLY)); - break; - } else if (t == com.gu.typesafe.config.impl.Tokens.END && !hadOpenCurly) { - putBack(t); - break; - } else if (flavor != com.gu.typesafe.config.ConfigSyntax.JSON && isIncludeKeyword(t)) { - ArrayList includeNodes = new ArrayList(); - includeNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - objectNodes.add(parseInclude(includeNodes)); - afterComma = false; - } else { - keyValueNodes = new ArrayList(); - com.gu.typesafe.config.impl.Token keyToken = t; - com.gu.typesafe.config.impl.ConfigNodePath path = parseKey(keyToken); - keyValueNodes.add(path); - com.gu.typesafe.config.impl.Token afterKey = nextTokenCollectingWhitespace(keyValueNodes); - boolean insideEquals = false; - - com.gu.typesafe.config.impl.AbstractConfigNodeValue nextValue; - if (flavor == com.gu.typesafe.config.ConfigSyntax.CONF && afterKey == com.gu.typesafe.config.impl.Tokens.OPEN_CURLY) { - // can omit the ':' or '=' before an object value - nextValue = parseValue(afterKey); - } else { - if (!isKeyValueSeparatorToken(afterKey)) { - throw parseError(addQuoteSuggestion(afterKey.toString(), - "Key '" + path.render() + "' may not be followed by token: " - + afterKey)); - } - - keyValueNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(afterKey)); - - if (afterKey == com.gu.typesafe.config.impl.Tokens.EQUALS) { - insideEquals = true; - equalsCount += 1; - } - - nextValue = consolidateValues(keyValueNodes); - if (nextValue == null) { - nextValue = parseValue(nextTokenCollectingWhitespace(keyValueNodes)); - } - } - - keyValueNodes.add(nextValue); - if (insideEquals) { - equalsCount -= 1; - } - lastInsideEquals = insideEquals; - - String key = path.value().first(); - Path remaining = path.value().remainder(); - - if (remaining == null) { - Boolean existing = keys.get(key); - if (existing != null) { - // In strict JSON, dups should be an error; while in - // our custom config language, they should be merged - // if the value is an object (or substitution that - // could become an object). - - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) { - throw parseError("JSON does not allow duplicate fields: '" - + key - + "' was already seen"); - } - } - keys.put(key, true); - } else { - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "somehow got multi-element path in JSON mode"); - } - keys.put(key, true); - } - - afterComma = false; - objectNodes.add(new ConfigNodeField(keyValueNodes)); - } - - if (checkElementSeparator(objectNodes)) { - // continue looping - afterComma = true; - } else { - t = nextTokenCollectingWhitespace(objectNodes); - if (t == com.gu.typesafe.config.impl.Tokens.CLOSE_CURLY) { - if (!hadOpenCurly) { - throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals, - t.toString(), "unbalanced close brace '}' with no open brace")); - } - objectNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - break; - } else if (hadOpenCurly) { - throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals, - t.toString(), "Expecting close brace } or a comma, got " + t)); - } else { - if (t == com.gu.typesafe.config.impl.Tokens.END) { - putBack(t); - break; - } else { - throw parseError(addQuoteSuggestion(lastPath, lastInsideEquals, - t.toString(), "Expecting end of input or a comma, got " + t)); - } - } - } - } - - return new com.gu.typesafe.config.impl.ConfigNodeObject(objectNodes); - } - - private com.gu.typesafe.config.impl.ConfigNodeComplexValue parseArray() { - ArrayList children = new ArrayList(); - children.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.OPEN_SQUARE)); - // invoked just after the OPEN_SQUARE - com.gu.typesafe.config.impl.Token t; - - com.gu.typesafe.config.impl.AbstractConfigNodeValue nextValue = consolidateValues(children); - if (nextValue != null) { - children.add(nextValue); - } else { - t = nextTokenCollectingWhitespace(children); - - // special-case the first element - if (t == com.gu.typesafe.config.impl.Tokens.CLOSE_SQUARE) { - children.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - return new ConfigNodeArray(children); - } else if (com.gu.typesafe.config.impl.Tokens.isValue(t) || t == com.gu.typesafe.config.impl.Tokens.OPEN_CURLY - || t == com.gu.typesafe.config.impl.Tokens.OPEN_SQUARE || com.gu.typesafe.config.impl.Tokens.isUnquotedText(t) - || com.gu.typesafe.config.impl.Tokens.isSubstitution(t)) { - nextValue = parseValue(t); - children.add(nextValue); - } else { - throw parseError("List should have ] or a first element after the open [, instead had token: " - + t - + " (if you want " - + t - + " to be part of a string value, then double-quote it)"); - } - } - - // now remaining elements - while (true) { - // just after a value - if (checkElementSeparator(children)) { - // comma (or newline equivalent) consumed - } else { - t = nextTokenCollectingWhitespace(children); - if (t == com.gu.typesafe.config.impl.Tokens.CLOSE_SQUARE) { - children.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(t)); - return new ConfigNodeArray(children); - } else { - throw parseError("List should have ended with ] or had a comma, instead had token: " - + t - + " (if you want " - + t - + " to be part of a string value, then double-quote it)"); - } - } - - // now just after a comma - nextValue = consolidateValues(children); - if (nextValue != null) { - children.add(nextValue); - } else { - t = nextTokenCollectingWhitespace(children); - if (com.gu.typesafe.config.impl.Tokens.isValue(t) || t == com.gu.typesafe.config.impl.Tokens.OPEN_CURLY - || t == com.gu.typesafe.config.impl.Tokens.OPEN_SQUARE || com.gu.typesafe.config.impl.Tokens.isUnquotedText(t) - || com.gu.typesafe.config.impl.Tokens.isSubstitution(t)) { - nextValue = parseValue(t); - children.add(nextValue); - } else if (flavor != com.gu.typesafe.config.ConfigSyntax.JSON && t == com.gu.typesafe.config.impl.Tokens.CLOSE_SQUARE) { - // we allow one trailing comma - putBack(t); - } else { - throw parseError("List should have had new element after a comma, instead had token: " - + t - + " (if you want the comma or " - + t - + " to be part of a string value, then double-quote it)"); - } - } - } - } - - com.gu.typesafe.config.impl.ConfigNodeRoot parse() { - ArrayList children = new ArrayList(); - com.gu.typesafe.config.impl.Token t = nextToken(); - if (t == com.gu.typesafe.config.impl.Tokens.START) { - // OK - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "token stream did not begin with START, had " + t); - } - - t = nextTokenCollectingWhitespace(children); - com.gu.typesafe.config.impl.AbstractConfigNode result = null; - boolean missingCurly = false; - if (t == com.gu.typesafe.config.impl.Tokens.OPEN_CURLY || t == com.gu.typesafe.config.impl.Tokens.OPEN_SQUARE) { - result = parseValue(t); - } else { - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) { - if (t == com.gu.typesafe.config.impl.Tokens.END) { - throw parseError("Empty document"); - } else { - throw parseError("Document must have an object or array at root, unexpected token: " - + t); - } - } else { - // the root object can omit the surrounding braces. - // this token should be the first field's key, or part - // of it, so put it back. - putBack(t); - missingCurly = true; - result = parseObject(false); - } - } - // Need to pull the children out of the resulting node so we can keep leading - // and trailing whitespace if this was a no-brace object. Otherwise, we need to add - // the result into the list of children. - if (result instanceof com.gu.typesafe.config.impl.ConfigNodeObject && missingCurly) { - children.addAll(((com.gu.typesafe.config.impl.ConfigNodeComplexValue) result).children()); - } else { - children.add(result); - } - t = nextTokenCollectingWhitespace(children); - if (t == com.gu.typesafe.config.impl.Tokens.END) { - if (missingCurly) { - // If there were no braces, the entire document should be treated as a single object - return new com.gu.typesafe.config.impl.ConfigNodeRoot(Collections.singletonList((com.gu.typesafe.config.impl.AbstractConfigNode)new com.gu.typesafe.config.impl.ConfigNodeObject(children)), baseOrigin); - } else { - return new com.gu.typesafe.config.impl.ConfigNodeRoot(children, baseOrigin); - } - } else { - throw parseError("Document has trailing tokens after first object or array: " - + t); - } - } - - // Parse a given input stream into a single value node. Used when doing a replace inside a ConfigDocument. - com.gu.typesafe.config.impl.AbstractConfigNodeValue parseSingleValue() { - com.gu.typesafe.config.impl.Token t = nextToken(); - if (t == com.gu.typesafe.config.impl.Tokens.START) { - // OK - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "token stream did not begin with START, had " + t); - } - - t = nextToken(); - if (com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(t) || com.gu.typesafe.config.impl.Tokens.isNewline(t) || isUnquotedWhitespace(t) || com.gu.typesafe.config.impl.Tokens.isComment(t)) { - throw parseError("The value from withValueText cannot have leading or trailing newlines, whitespace, or comments"); - } - if (t == com.gu.typesafe.config.impl.Tokens.END) { - throw parseError("Empty value"); - } - if (flavor == ConfigSyntax.JSON) { - com.gu.typesafe.config.impl.AbstractConfigNodeValue node = parseValue(t); - t = nextToken(); - if (t == com.gu.typesafe.config.impl.Tokens.END) { - return node; - } else { - throw parseError("Parsing JSON and the value set in withValueText was either a concatenation or " + - "had trailing whitespace, newlines, or comments"); - } - } else { - putBack(t); - ArrayList nodes = new ArrayList(); - com.gu.typesafe.config.impl.AbstractConfigNodeValue node = consolidateValues(nodes); - t = nextToken(); - if (t == com.gu.typesafe.config.impl.Tokens.END) { - return node; - } else { - throw parseError("The value from withValueText cannot have leading or trailing newlines, whitespace, or comments"); - } - } - } - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDouble.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDouble.java deleted file mode 100644 index 4438fb3e58..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigDouble.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.ObjectStreamException; -import java.io.Serializable; - -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigValueType; - -final class ConfigDouble extends ConfigNumber implements Serializable { - - private static final long serialVersionUID = 2L; - - final private double value; - - ConfigDouble(com.gu.typesafe.config.ConfigOrigin origin, double value, String originalText) { - super(origin, originalText); - this.value = value; - } - - @Override - public com.gu.typesafe.config.ConfigValueType valueType() { - return ConfigValueType.NUMBER; - } - - @Override - public Double unwrapped() { - return value; - } - - @Override - String transformToString() { - String s = super.transformToString(); - if (s == null) - return Double.toString(value); - else - return s; - } - - @Override - protected long longValue() { - return (long) value; - } - - @Override - protected double doubleValue() { - return value; - } - - @Override - protected ConfigDouble newCopy(ConfigOrigin origin) { - return new ConfigDouble(origin, value, originalText); - } - - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new com.gu.typesafe.config.impl.SerializedConfigValue(this); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigImpl.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigImpl.java deleted file mode 100644 index 9770b3ed54..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigImpl.java +++ /dev/null @@ -1,539 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.File; -import java.lang.ref.WeakReference; -import java.net.URL; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.Callable; - -import com.gu.typesafe.config.*; -import com.gu.typesafe.config.ConfigMemorySize; - -/** - * Internal implementation detail, not ABI stable, do not touch. - * For use only by the {@link com.typesafe.config} package. - */ -public class ConfigImpl { - private static final String ENV_VAR_OVERRIDE_PREFIX = "CONFIG_FORCE_"; - - private static class LoaderCache { - private com.gu.typesafe.config.Config currentSystemProperties; - private WeakReference currentLoader; - private Map cache; - - LoaderCache() { - this.currentSystemProperties = null; - this.currentLoader = new WeakReference(null); - this.cache = new HashMap(); - } - - // for now, caching as long as the loader remains the same, - // drop entire cache if it changes. - synchronized com.gu.typesafe.config.Config getOrElseUpdate(ClassLoader loader, String key, Callable updater) { - if (loader != currentLoader.get()) { - // reset the cache if we start using a different loader - cache.clear(); - currentLoader = new WeakReference(loader); - } - - com.gu.typesafe.config.Config systemProperties = systemPropertiesAsConfig(); - if (systemProperties != currentSystemProperties) { - cache.clear(); - currentSystemProperties = systemProperties; - } - - com.gu.typesafe.config.Config config = cache.get(key); - if (config == null) { - try { - config = updater.call(); - } catch (RuntimeException e) { - throw e; // this will include ConfigException - } catch (Exception e) { - throw new com.gu.typesafe.config.ConfigException.Generic(e.getMessage(), e); - } - if (config == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("null config from cache updater"); - cache.put(key, config); - } - - return config; - } - } - - private static class LoaderCacheHolder { - static final LoaderCache cache = new LoaderCache(); - } - - public static com.gu.typesafe.config.Config computeCachedConfig(ClassLoader loader, String key, - Callable updater) { - LoaderCache cache; - try { - cache = LoaderCacheHolder.cache; - } catch (ExceptionInInitializerError e) { - throw ConfigImplUtil.extractInitializerError(e); - } - return cache.getOrElseUpdate(loader, key, updater); - } - - - static class FileNameSource implements SimpleIncluder.NameSource { - @Override - public com.gu.typesafe.config.ConfigParseable nameToParseable(String name, com.gu.typesafe.config.ConfigParseOptions parseOptions) { - return com.gu.typesafe.config.impl.Parseable.newFile(new File(name), parseOptions); - } - }; - - static class ClasspathNameSource implements SimpleIncluder.NameSource { - @Override - public com.gu.typesafe.config.ConfigParseable nameToParseable(String name, com.gu.typesafe.config.ConfigParseOptions parseOptions) { - return com.gu.typesafe.config.impl.Parseable.newResources(name, parseOptions); - } - }; - - static class ClasspathNameSourceWithClass implements SimpleIncluder.NameSource { - final private Class klass; - - public ClasspathNameSourceWithClass(Class klass) { - this.klass = klass; - } - - @Override - public ConfigParseable nameToParseable(String name, com.gu.typesafe.config.ConfigParseOptions parseOptions) { - return com.gu.typesafe.config.impl.Parseable.newResources(klass, name, parseOptions); - } - }; - - public static com.gu.typesafe.config.ConfigObject parseResourcesAnySyntax(Class klass, String resourceBasename, - com.gu.typesafe.config.ConfigParseOptions baseOptions) { - SimpleIncluder.NameSource source = new ClasspathNameSourceWithClass(klass); - return SimpleIncluder.fromBasename(source, resourceBasename, baseOptions); - } - - public static com.gu.typesafe.config.ConfigObject parseResourcesAnySyntax(String resourceBasename, - com.gu.typesafe.config.ConfigParseOptions baseOptions) { - SimpleIncluder.NameSource source = new ClasspathNameSource(); - return SimpleIncluder.fromBasename(source, resourceBasename, baseOptions); - } - - public static com.gu.typesafe.config.ConfigObject parseFileAnySyntax(File basename, com.gu.typesafe.config.ConfigParseOptions baseOptions) { - SimpleIncluder.NameSource source = new FileNameSource(); - return SimpleIncluder.fromBasename(source, basename.getPath(), baseOptions); - } - - static AbstractConfigObject emptyObject(String originDescription) { - com.gu.typesafe.config.ConfigOrigin origin = originDescription != null ? SimpleConfigOrigin - .newSimple(originDescription) : null; - return emptyObject(origin); - } - - public static com.gu.typesafe.config.Config emptyConfig(String originDescription) { - return emptyObject(originDescription).toConfig(); - } - - static AbstractConfigObject empty(com.gu.typesafe.config.ConfigOrigin origin) { - return emptyObject(origin); - } - - // default origin for values created with fromAnyRef and no origin specified - final private static com.gu.typesafe.config.ConfigOrigin defaultValueOrigin = SimpleConfigOrigin - .newSimple("hardcoded value"); - final private static ConfigBoolean defaultTrueValue = new ConfigBoolean( - defaultValueOrigin, true); - final private static ConfigBoolean defaultFalseValue = new ConfigBoolean( - defaultValueOrigin, false); - final private static com.gu.typesafe.config.impl.ConfigNull defaultNullValue = new com.gu.typesafe.config.impl.ConfigNull( - defaultValueOrigin); - final private static SimpleConfigList defaultEmptyList = new SimpleConfigList( - defaultValueOrigin, Collections. emptyList()); - final private static SimpleConfigObject defaultEmptyObject = SimpleConfigObject - .empty(defaultValueOrigin); - - private static SimpleConfigList emptyList(com.gu.typesafe.config.ConfigOrigin origin) { - if (origin == null || origin == defaultValueOrigin) - return defaultEmptyList; - else - return new SimpleConfigList(origin, - Collections. emptyList()); - } - - private static AbstractConfigObject emptyObject(com.gu.typesafe.config.ConfigOrigin origin) { - // we want null origin to go to SimpleConfigObject.empty() to get the - // origin "empty config" rather than "hardcoded value" - if (origin == defaultValueOrigin) - return defaultEmptyObject; - else - return SimpleConfigObject.empty(origin); - } - - private static com.gu.typesafe.config.ConfigOrigin valueOrigin(String originDescription) { - if (originDescription == null) - return defaultValueOrigin; - else - return SimpleConfigOrigin.newSimple(originDescription); - } - - public static ConfigValue fromAnyRef(Object object, String originDescription) { - com.gu.typesafe.config.ConfigOrigin origin = valueOrigin(originDescription); - return fromAnyRef(object, origin, FromMapMode.KEYS_ARE_KEYS); - } - - public static com.gu.typesafe.config.ConfigObject fromPathMap( - Map pathMap, String originDescription) { - com.gu.typesafe.config.ConfigOrigin origin = valueOrigin(originDescription); - return (ConfigObject) fromAnyRef(pathMap, origin, - FromMapMode.KEYS_ARE_PATHS); - } - - static com.gu.typesafe.config.impl.AbstractConfigValue fromAnyRef(Object object, com.gu.typesafe.config.ConfigOrigin origin, - FromMapMode mapMode) { - if (origin == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "origin not supposed to be null"); - - if (object == null) { - if (origin != defaultValueOrigin) - return new com.gu.typesafe.config.impl.ConfigNull(origin); - else - return defaultNullValue; - } else if(object instanceof com.gu.typesafe.config.impl.AbstractConfigValue) { - return (com.gu.typesafe.config.impl.AbstractConfigValue) object; - } else if (object instanceof Boolean) { - if (origin != defaultValueOrigin) { - return new ConfigBoolean(origin, (Boolean) object); - } else if ((Boolean) object) { - return defaultTrueValue; - } else { - return defaultFalseValue; - } - } else if (object instanceof String) { - return new ConfigString.Quoted(origin, (String) object); - } else if (object instanceof Number) { - // here we always keep the same type that was passed to us, - // rather than figuring out if a Long would fit in an Int - // or a Double has no fractional part. i.e. deliberately - // not using ConfigNumber.newNumber() when we have a - // Double, Integer, or Long. - if (object instanceof Double) { - return new com.gu.typesafe.config.impl.ConfigDouble(origin, (Double) object, null); - } else if (object instanceof Integer) { - return new com.gu.typesafe.config.impl.ConfigInt(origin, (Integer) object, null); - } else if (object instanceof Long) { - return new ConfigLong(origin, (Long) object, null); - } else { - return com.gu.typesafe.config.impl.ConfigNumber.newNumber(origin, - ((Number) object).doubleValue(), null); - } - } else if (object instanceof Duration) { - return new ConfigLong(origin, ((Duration) object).toMillis(), null); - } else if (object instanceof Map) { - if (((Map) object).isEmpty()) - return emptyObject(origin); - - if (mapMode == FromMapMode.KEYS_ARE_KEYS) { - Map values = new HashMap(); - for (Map.Entry entry : ((Map) object).entrySet()) { - Object key = entry.getKey(); - if (!(key instanceof String)) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "bug in method caller: not valid to create ConfigObject from map with non-String key: " - + key); - com.gu.typesafe.config.impl.AbstractConfigValue value = fromAnyRef(entry.getValue(), - origin, mapMode); - values.put((String) key, value); - } - - return new SimpleConfigObject(origin, values); - } else { - return PropertiesParser.fromPathMap(origin, (Map) object); - } - } else if (object instanceof Iterable) { - Iterator i = ((Iterable) object).iterator(); - if (!i.hasNext()) - return emptyList(origin); - - List values = new ArrayList(); - while (i.hasNext()) { - com.gu.typesafe.config.impl.AbstractConfigValue v = fromAnyRef(i.next(), origin, mapMode); - values.add(v); - } - - return new SimpleConfigList(origin, values); - } else if (object instanceof ConfigMemorySize) { - return new ConfigLong(origin, ((ConfigMemorySize) object).toBytes(), null); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "bug in method caller: not valid to create ConfigValue from: " - + object); - } - } - - private static class DefaultIncluderHolder { - static final com.gu.typesafe.config.ConfigIncluder defaultIncluder = new SimpleIncluder(null); - } - - static ConfigIncluder defaultIncluder() { - try { - return DefaultIncluderHolder.defaultIncluder; - } catch (ExceptionInInitializerError e) { - throw ConfigImplUtil.extractInitializerError(e); - } - } - - private static Properties getSystemProperties() { - // Avoid ConcurrentModificationException due to parallel setting of system properties by copying properties - final Properties systemProperties = System.getProperties(); - final Properties systemPropertiesCopy = new Properties(); - synchronized (systemProperties) { - for (Map.Entry entry: systemProperties.entrySet()) { - // Java 11 introduces 'java.version.date', but we don't want that to - // overwrite 'java.version' - if (!entry.getKey().toString().startsWith("java.version.")) { - systemPropertiesCopy.put(entry.getKey(), entry.getValue()); - } - } - } - return systemPropertiesCopy; - } - - private static AbstractConfigObject loadSystemProperties() { - return (AbstractConfigObject) com.gu.typesafe.config.impl.Parseable.newProperties(getSystemProperties(), - com.gu.typesafe.config.ConfigParseOptions.defaults().setOriginDescription("system properties")).parse(); - } - - private static class SystemPropertiesHolder { - // this isn't final due to the reloadSystemPropertiesConfig() hack below - static volatile AbstractConfigObject systemProperties = loadSystemProperties(); - } - - static AbstractConfigObject systemPropertiesAsConfigObject() { - try { - return SystemPropertiesHolder.systemProperties; - } catch (ExceptionInInitializerError e) { - throw ConfigImplUtil.extractInitializerError(e); - } - } - - public static com.gu.typesafe.config.Config systemPropertiesAsConfig() { - return systemPropertiesAsConfigObject().toConfig(); - } - - public static void reloadSystemPropertiesConfig() { - // ConfigFactory.invalidateCaches() relies on this having the side - // effect that it drops all caches - SystemPropertiesHolder.systemProperties = loadSystemProperties(); - } - - private static AbstractConfigObject loadEnvVariables() { - return PropertiesParser.fromStringMap(newSimpleOrigin("env variables"), System.getenv()); - } - - private static class EnvVariablesHolder { - static volatile AbstractConfigObject envVariables = loadEnvVariables(); - } - - static AbstractConfigObject envVariablesAsConfigObject() { - try { - return EnvVariablesHolder.envVariables; - } catch (ExceptionInInitializerError e) { - throw ConfigImplUtil.extractInitializerError(e); - } - } - - public static com.gu.typesafe.config.Config envVariablesAsConfig() { - return envVariablesAsConfigObject().toConfig(); - } - - public static void reloadEnvVariablesConfig() { - // ConfigFactory.invalidateCaches() relies on this having the side - // effect that it drops all caches - EnvVariablesHolder.envVariables = loadEnvVariables(); - } - - - - private static AbstractConfigObject loadEnvVariablesOverrides() { - Map env = new HashMap(System.getenv()); - Map result = new HashMap(); - - for (String key : env.keySet()) { - if (key.startsWith(ENV_VAR_OVERRIDE_PREFIX)) { - result.put(ConfigImplUtil.envVariableAsProperty(key, ENV_VAR_OVERRIDE_PREFIX), env.get(key)); - } - } - - return PropertiesParser.fromStringMap(newSimpleOrigin("env variables overrides"), result); - } - - private static class EnvVariablesOverridesHolder { - static volatile AbstractConfigObject envVariables = loadEnvVariablesOverrides(); - } - - static AbstractConfigObject envVariablesOverridesAsConfigObject() { - try { - return EnvVariablesOverridesHolder.envVariables; - } catch (ExceptionInInitializerError e) { - throw ConfigImplUtil.extractInitializerError(e); - } - } - - public static com.gu.typesafe.config.Config envVariablesOverridesAsConfig() { - return envVariablesOverridesAsConfigObject().toConfig(); - } - - public static void reloadEnvVariablesOverridesConfig() { - // ConfigFactory.invalidateCaches() relies on this having the side - // effect that it drops all caches - EnvVariablesOverridesHolder.envVariables = loadEnvVariablesOverrides(); - } - - public static com.gu.typesafe.config.Config defaultReference(final ClassLoader loader) { - return computeCachedConfig(loader, "defaultReference", new Callable() { - @Override - public com.gu.typesafe.config.Config call() { - com.gu.typesafe.config.Config unresolvedResources = unresolvedReference(loader); - return systemPropertiesAsConfig().withFallback(unresolvedResources).resolve(); - } - }); - } - - private static com.gu.typesafe.config.Config unresolvedReference(final ClassLoader loader) { - return computeCachedConfig(loader, "unresolvedReference", new Callable() { - @Override - public com.gu.typesafe.config.Config call() { - return Parseable.newResources("reference.conf", - ConfigParseOptions.defaults().setClassLoader(loader)) - .parse().toConfig(); - } - }); - } - - /** - * This returns the unresolved reference configuration, but before doing so, - * it verifies that the reference configuration resolves, to ensure that it - * is self contained and doesn't depend on any higher level configuration - * files. - */ - public static Config defaultReferenceUnresolved(final ClassLoader loader) { - // First, verify that `reference.conf` resolves by itself. - try { - defaultReference(loader); - } catch (com.gu.typesafe.config.ConfigException.UnresolvedSubstitution e) { - throw e.addExtraDetail("Could not resolve substitution in reference.conf to a value: %s. All reference.conf files are required to be fully, independently resolvable, and should not require the presence of values for substitutions from further up the hierarchy."); - } - // Now load the unresolved version - return unresolvedReference(loader); - } - - - private static class DebugHolder { - private static String LOADS = "loads"; - private static String SUBSTITUTIONS = "substitutions"; - - private static Map loadDiagnostics() { - Map result = new HashMap(); - result.put(LOADS, false); - result.put(SUBSTITUTIONS, false); - - // People do -Dconfig.trace=foo,bar to enable tracing of different things - String s = System.getProperty("config.trace"); - if (s == null) { - return result; - } else { - String[] keys = s.split(","); - for (String k : keys) { - if (k.equals(LOADS)) { - result.put(LOADS, true); - } else if (k.equals(SUBSTITUTIONS)) { - result.put(SUBSTITUTIONS, true); - } else { - System.err.println("config.trace property contains unknown trace topic '" - + k + "'"); - } - } - return result; - } - } - - private static final Map diagnostics = loadDiagnostics(); - - private static final boolean traceLoadsEnabled = diagnostics.get(LOADS); - private static final boolean traceSubstitutionsEnabled = diagnostics.get(SUBSTITUTIONS); - - static boolean traceLoadsEnabled() { - return traceLoadsEnabled; - } - - static boolean traceSubstitutionsEnabled() { - return traceSubstitutionsEnabled; - } - } - - public static boolean traceLoadsEnabled() { - try { - return DebugHolder.traceLoadsEnabled(); - } catch (ExceptionInInitializerError e) { - throw ConfigImplUtil.extractInitializerError(e); - } - } - - public static boolean traceSubstitutionsEnabled() { - try { - return DebugHolder.traceSubstitutionsEnabled(); - } catch (ExceptionInInitializerError e) { - throw ConfigImplUtil.extractInitializerError(e); - } - } - - public static void trace(String message) { - System.err.println(message); - } - - public static void trace(int indentLevel, String message) { - while (indentLevel > 0) { - System.err.print(" "); - indentLevel -= 1; - } - System.err.println(message); - } - - // the basic idea here is to add the "what" and have a canonical - // toplevel error message. the "original" exception may however have extra - // detail about what happened. call this if you have a better "what" than - // further down on the stack. - static com.gu.typesafe.config.ConfigException.NotResolved improveNotResolved(Path what, - com.gu.typesafe.config.ConfigException.NotResolved original) { - String newMessage = what.render() - + " has not been resolved, you need to call Config#resolve()," - + " see API docs for Config#resolve()"; - if (newMessage.equals(original.getMessage())) - return original; - else - return new ConfigException.NotResolved(newMessage, original); - } - - public static com.gu.typesafe.config.ConfigOrigin newSimpleOrigin(String description) { - if (description == null) { - return defaultValueOrigin; - } else { - return SimpleConfigOrigin.newSimple(description); - } - } - - public static com.gu.typesafe.config.ConfigOrigin newFileOrigin(String filename) { - return SimpleConfigOrigin.newFile(filename); - } - - public static ConfigOrigin newURLOrigin(URL url) { - return SimpleConfigOrigin.newURL(url); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigImplUtil.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigImplUtil.java deleted file mode 100644 index 17450e6885..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigImplUtil.java +++ /dev/null @@ -1,302 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.DataOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigSyntax; - -/** - * Internal implementation detail, not ABI stable, do not touch. - * For use only by the {@link com.typesafe.config} package. - */ -final public class ConfigImplUtil { - static boolean equalsHandlingNull(Object a, Object b) { - if (a == null && b != null) - return false; - else if (a != null && b == null) - return false; - else if (a == b) // catches null == null plus optimizes identity case - return true; - else - return a.equals(b); - } - - static boolean isC0Control(int codepoint) { - return (codepoint >= 0x0000 && codepoint <= 0x001F); - } - - public static String renderJsonString(String s) { - StringBuilder sb = new StringBuilder(); - sb.append('"'); - for (int i = 0; i < s.length(); ++i) { - char c = s.charAt(i); - switch (c) { - case '"': - sb.append("\\\""); - break; - case '\\': - sb.append("\\\\"); - break; - case '\n': - sb.append("\\n"); - break; - case '\b': - sb.append("\\b"); - break; - case '\f': - sb.append("\\f"); - break; - case '\r': - sb.append("\\r"); - break; - case '\t': - sb.append("\\t"); - break; - default: - if (isC0Control(c)) - sb.append(String.format("\\u%04x", (int) c)); - else - sb.append(c); - } - } - sb.append('"'); - return sb.toString(); - } - - static String renderStringUnquotedIfPossible(String s) { - // this can quote unnecessarily as long as it never fails to quote when - // necessary - if (s.length() == 0) - return renderJsonString(s); - - // if it starts with a hyphen or number, we have to quote - // to ensure we end up with a string and not a number - int first = s.codePointAt(0); - if (Character.isDigit(first) || first == '-') - return renderJsonString(s); - - if (s.startsWith("include") || s.startsWith("true") || s.startsWith("false") - || s.startsWith("null") || s.contains("//")) - return renderJsonString(s); - - // only unquote if it's pure alphanumeric - for (int i = 0; i < s.length(); ++i) { - char c = s.charAt(i); - if (!(Character.isLetter(c) || Character.isDigit(c) || c == '-')) - return renderJsonString(s); - } - - return s; - } - - static boolean isWhitespace(int codepoint) { - switch (codepoint) { - // try to hit the most common ASCII ones first, then the nonbreaking - // spaces that Java brokenly leaves out of isWhitespace. - case ' ': - case '\n': - case '\u00A0': - case '\u2007': - case '\u202F': - // this one is the BOM, see - // http://www.unicode.org/faq/utf_bom.html#BOM - // we just accept it as a zero-width nonbreaking space. - case '\uFEFF': - return true; - default: - return Character.isWhitespace(codepoint); - } - } - - public static String unicodeTrim(String s) { - // this is dumb because it looks like there aren't any whitespace - // characters that need surrogate encoding. But, points for - // pedantic correctness! It's future-proof or something. - // String.trim() actually is broken, since there are plenty of - // non-ASCII whitespace characters. - final int length = s.length(); - if (length == 0) - return s; - - int start = 0; - while (start < length) { - char c = s.charAt(start); - if (c == ' ' || c == '\n') { - start += 1; - } else { - int cp = s.codePointAt(start); - if (isWhitespace(cp)) - start += Character.charCount(cp); - else - break; - } - } - - int end = length; - while (end > start) { - char c = s.charAt(end - 1); - if (c == ' ' || c == '\n') { - --end; - } else { - int cp; - int delta; - if (Character.isLowSurrogate(c)) { - cp = s.codePointAt(end - 2); - delta = 2; - } else { - cp = s.codePointAt(end - 1); - delta = 1; - } - if (isWhitespace(cp)) - end -= delta; - else - break; - } - } - return s.substring(start, end); - } - - - public static com.gu.typesafe.config.ConfigException extractInitializerError(ExceptionInInitializerError e) { - Throwable cause = e.getCause(); - if (cause != null && cause instanceof com.gu.typesafe.config.ConfigException) { - return (com.gu.typesafe.config.ConfigException) cause; - } else { - throw e; - } - } - - static File urlToFile(URL url) { - // this isn't really right, clearly, but not sure what to do. - try { - // this will properly handle hex escapes, etc. - return new File(url.toURI()); - } catch (URISyntaxException e) { - // this handles some stuff like file:///c:/Whatever/ - // apparently but mangles handling of hex escapes - return new File(url.getPath()); - } catch (IllegalArgumentException e) { - // file://foo with double slash causes - // IllegalArgumentException "url has an authority component" - return new File(url.getPath()); - } - } - - public static String joinPath(String... elements) { - return (new Path(elements)).render(); - } - - public static String joinPath(List elements) { - return joinPath(elements.toArray(new String[0])); - } - - public static List splitPath(String path) { - Path p = Path.newPath(path); - List elements = new ArrayList(); - while (p != null) { - elements.add(p.first()); - p = p.remainder(); - } - return elements; - } - - public static com.gu.typesafe.config.ConfigOrigin readOrigin(ObjectInputStream in) throws IOException { - return com.gu.typesafe.config.impl.SerializedConfigValue.readOrigin(in, null); - } - - public static void writeOrigin(ObjectOutputStream out, ConfigOrigin origin) throws IOException { - com.gu.typesafe.config.impl.SerializedConfigValue.writeOrigin(new DataOutputStream(out), (com.gu.typesafe.config.impl.SimpleConfigOrigin) origin, - null); - } - - static String toCamelCase(String originalName) { - String[] words = originalName.split("-+"); - StringBuilder nameBuilder = new StringBuilder(originalName.length()); - for (String word : words) { - if (nameBuilder.length() == 0) { - nameBuilder.append(word); - } else { - nameBuilder.append(word.substring(0, 1).toUpperCase()); - nameBuilder.append(word.substring(1)); - } - } - return nameBuilder.toString(); - } - - private static char underscoreMappings(int num) { - // Rationale on name mangling: - // - // Most shells (e.g. bash, sh, etc.) doesn't support any character other - // than alphanumeric and `_` in environment variables names. - // In HOCON the default separator is `.` so it is directly translated to a - // single `_` for convenience; `-` and `_` are less often present in config - // keys but they have to be representable and the only possible mapping is - // `_` repeated. - switch (num) { - case 1: return '.'; - case 2: return '-'; - case 3: return '_'; - default: return 0; - } - } - - static String envVariableAsProperty(String variable, String prefix) throws com.gu.typesafe.config.ConfigException { - StringBuilder builder = new StringBuilder(); - - String strippedPrefix = variable.substring(prefix.length(), variable.length()); - - int underscores = 0; - for (char c : strippedPrefix.toCharArray()) { - if (c == '_') { - underscores++; - } else { - if (underscores > 0 && underscores < 4) { - builder.append(underscoreMappings(underscores)); - } else if (underscores > 3) { - throw new com.gu.typesafe.config.ConfigException.BadPath(variable, "Environment variable contains an un-mapped number of underscores."); - } - underscores = 0; - builder.append(c); - } - } - - if (underscores > 0 && underscores < 4) { - builder.append(underscoreMappings(underscores)); - } else if (underscores > 3) { - throw new ConfigException.BadPath(variable, "Environment variable contains an un-mapped number of underscores."); - } - - return builder.toString(); - } - - /** - * Guess configuration syntax from given filename. - * - * @param filename configuration filename - * @return configuration syntax if a match is found. Otherwise, null. - */ - public static com.gu.typesafe.config.ConfigSyntax syntaxFromExtension(String filename) { - if (filename == null) - return null; - else if (filename.endsWith(".json")) - return com.gu.typesafe.config.ConfigSyntax.JSON; - else if (filename.endsWith(".conf")) - return com.gu.typesafe.config.ConfigSyntax.CONF; - else if (filename.endsWith(".properties")) - return ConfigSyntax.PROPERTIES; - else - return null; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigIncludeKind.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigIncludeKind.java deleted file mode 100644 index 4ba2530d1e..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigIncludeKind.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.gu.typesafe.config.impl; - -enum ConfigIncludeKind { - URL, FILE, CLASSPATH, HEURISTIC -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigInt.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigInt.java deleted file mode 100644 index 5b8ebf8eb0..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigInt.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.ObjectStreamException; -import java.io.Serializable; - -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigValueType; - -final class ConfigInt extends ConfigNumber implements Serializable { - - private static final long serialVersionUID = 2L; - - final private int value; - - ConfigInt(com.gu.typesafe.config.ConfigOrigin origin, int value, String originalText) { - super(origin, originalText); - this.value = value; - } - - @Override - public com.gu.typesafe.config.ConfigValueType valueType() { - return ConfigValueType.NUMBER; - } - - @Override - public Integer unwrapped() { - return value; - } - - @Override - String transformToString() { - String s = super.transformToString(); - if (s == null) - return Integer.toString(value); - else - return s; - } - - @Override - protected long longValue() { - return value; - } - - @Override - protected double doubleValue() { - return value; - } - - @Override - protected ConfigInt newCopy(ConfigOrigin origin) { - return new ConfigInt(origin, value, originalText); - } - - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new SerializedConfigValue(this); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigLong.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigLong.java deleted file mode 100644 index 6d6f92acc9..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigLong.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.ObjectStreamException; -import java.io.Serializable; - -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigValueType; - -final class ConfigLong extends com.gu.typesafe.config.impl.ConfigNumber implements Serializable { - - private static final long serialVersionUID = 2L; - - final private long value; - - ConfigLong(com.gu.typesafe.config.ConfigOrigin origin, long value, String originalText) { - super(origin, originalText); - this.value = value; - } - - @Override - public com.gu.typesafe.config.ConfigValueType valueType() { - return ConfigValueType.NUMBER; - } - - @Override - public Long unwrapped() { - return value; - } - - @Override - String transformToString() { - String s = super.transformToString(); - if (s == null) - return Long.toString(value); - else - return s; - } - - @Override - protected long longValue() { - return value; - } - - @Override - protected double doubleValue() { - return value; - } - - @Override - protected ConfigLong newCopy(ConfigOrigin origin) { - return new ConfigLong(origin, value, originalText); - } - - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new com.gu.typesafe.config.impl.SerializedConfigValue(this); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeArray.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeArray.java deleted file mode 100644 index e526ddad86..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeArray.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gu.typesafe.config.impl; - -import java.util.Collection; - -final class ConfigNodeArray extends com.gu.typesafe.config.impl.ConfigNodeComplexValue { - ConfigNodeArray(Collection children) { - super(children); - } - - @Override - protected ConfigNodeArray newNode(Collection nodes) { - return new ConfigNodeArray(nodes); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeComment.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeComment.java deleted file mode 100644 index de4e8ada16..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeComment.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gu.typesafe.config.impl; - - -import com.gu.typesafe.config.ConfigException; - -final class ConfigNodeComment extends ConfigNodeSingleToken { - ConfigNodeComment(Token comment) { - super(comment); - if (!Tokens.isComment(super.token)) { - throw new ConfigException.BugOrBroken("Tried to create a ConfigNodeComment from a non-comment token"); - } - } - - protected String commentText() { - return Tokens.getCommentText(super.token); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeComplexValue.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeComplexValue.java deleted file mode 100644 index 3d2a509cd3..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeComplexValue.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.*; - -abstract class ConfigNodeComplexValue extends AbstractConfigNodeValue { - final protected ArrayList children; - - ConfigNodeComplexValue(Collection children) { - this.children = new ArrayList(children); - } - - final public Collection children() { - return children; - } - - @Override - protected Collection tokens() { - ArrayList tokens = new ArrayList(); - for (AbstractConfigNode child : children) { - tokens.addAll(child.tokens()); - } - return tokens; - } - - protected ConfigNodeComplexValue indentText(AbstractConfigNode indentation) { - ArrayList childrenCopy = new ArrayList(children); - for (int i = 0; i < childrenCopy.size(); i++) { - AbstractConfigNode child = childrenCopy.get(i); - if (child instanceof ConfigNodeSingleToken && - Tokens.isNewline(((ConfigNodeSingleToken) child).token())) { - childrenCopy.add(i + 1, indentation); - i++; - } else if (child instanceof ConfigNodeField) { - AbstractConfigNode value = ((ConfigNodeField) child).value(); - if (value instanceof ConfigNodeComplexValue) { - childrenCopy.set(i, ((ConfigNodeField) child).replaceValue(((ConfigNodeComplexValue) value).indentText(indentation))); - } - } else if (child instanceof ConfigNodeComplexValue) { - childrenCopy.set(i, ((ConfigNodeComplexValue) child).indentText(indentation)); - } - } - return newNode(childrenCopy); - } - - // This method will just call into the object's constructor, but it's needed - // for use in the indentText() method so we can avoid a gross if/else statement - // checking the type of this - abstract ConfigNodeComplexValue newNode(Collection nodes); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeConcatenation.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeConcatenation.java deleted file mode 100644 index 3d81a6f0ab..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeConcatenation.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gu.typesafe.config.impl; - -import java.util.Collection; - -final class ConfigNodeConcatenation extends com.gu.typesafe.config.impl.ConfigNodeComplexValue { - ConfigNodeConcatenation(Collection children) { - super(children); - } - - @Override - protected ConfigNodeConcatenation newNode(Collection nodes) { - return new ConfigNodeConcatenation(nodes); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeField.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeField.java deleted file mode 100644 index 86c2260ea3..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeField.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigException; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -final class ConfigNodeField extends com.gu.typesafe.config.impl.AbstractConfigNode { - final private ArrayList children; - - public ConfigNodeField(Collection children) { - this.children = new ArrayList(children); - } - - @Override - protected Collection tokens() { - ArrayList tokens = new ArrayList(); - for (com.gu.typesafe.config.impl.AbstractConfigNode child : children) { - tokens.addAll(child.tokens()); - } - return tokens; - } - - public ConfigNodeField replaceValue(com.gu.typesafe.config.impl.AbstractConfigNodeValue newValue) { - ArrayList childrenCopy = new ArrayList(children); - for (int i = 0; i < childrenCopy.size(); i++) { - if (childrenCopy.get(i) instanceof com.gu.typesafe.config.impl.AbstractConfigNodeValue) { - childrenCopy.set(i, newValue); - return new ConfigNodeField(childrenCopy); - } - } - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Field node doesn't have a value"); - } - - public com.gu.typesafe.config.impl.AbstractConfigNodeValue value() { - for (int i = 0; i < children.size(); i++) { - if (children.get(i) instanceof com.gu.typesafe.config.impl.AbstractConfigNodeValue) { - return (com.gu.typesafe.config.impl.AbstractConfigNodeValue)children.get(i); - } - } - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Field node doesn't have a value"); - } - - public com.gu.typesafe.config.impl.ConfigNodePath path() { - for (int i = 0; i < children.size(); i++) { - if (children.get(i) instanceof com.gu.typesafe.config.impl.ConfigNodePath) { - return (com.gu.typesafe.config.impl.ConfigNodePath)children.get(i); - } - } - throw new ConfigException.BugOrBroken("Field node doesn't have a path"); - } - - protected com.gu.typesafe.config.impl.Token separator() { - for (com.gu.typesafe.config.impl.AbstractConfigNode child : children) { - if (child instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken) { - com.gu.typesafe.config.impl.Token t = ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) child).token(); - if (t == com.gu.typesafe.config.impl.Tokens.PLUS_EQUALS || t == com.gu.typesafe.config.impl.Tokens.COLON || t == com.gu.typesafe.config.impl.Tokens.EQUALS) { - return t; - } - } - } - return null; - } - - protected List comments() { - List comments = new ArrayList(); - for (com.gu.typesafe.config.impl.AbstractConfigNode child : children) { - if (child instanceof com.gu.typesafe.config.impl.ConfigNodeComment) { - comments.add(((com.gu.typesafe.config.impl.ConfigNodeComment) child).commentText()); - } - } - return comments; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeInclude.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeInclude.java deleted file mode 100644 index 8b2ed56ee1..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeInclude.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gu.typesafe.config.impl; - -import java.util.ArrayList; -import java.util.Collection; - -final class ConfigNodeInclude extends com.gu.typesafe.config.impl.AbstractConfigNode { - final private ArrayList children; - final private ConfigIncludeKind kind; - final private boolean isRequired; - - ConfigNodeInclude(Collection children, ConfigIncludeKind kind, boolean isRequired) { - this.children = new ArrayList(children); - this.kind = kind; - this.isRequired = isRequired; - } - - final public Collection children() { - return children; - } - - @Override - protected Collection tokens() { - ArrayList tokens = new ArrayList(); - for (com.gu.typesafe.config.impl.AbstractConfigNode child : children) { - tokens.addAll(child.tokens()); - } - return tokens; - } - - protected ConfigIncludeKind kind() { - return kind; - } - - protected boolean isRequired() { - return isRequired; - } - - protected String name() { - for (com.gu.typesafe.config.impl.AbstractConfigNode n : children) { - if (n instanceof com.gu.typesafe.config.impl.ConfigNodeSimpleValue) { - return (String) com.gu.typesafe.config.impl.Tokens.getValue(((com.gu.typesafe.config.impl.ConfigNodeSimpleValue) n).token()).unwrapped(); - } - } - return null; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeObject.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeObject.java deleted file mode 100644 index 30d260416f..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeObject.java +++ /dev/null @@ -1,281 +0,0 @@ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigSyntax; - -import java.util.ArrayList; -import java.util.Collection; - -final class ConfigNodeObject extends com.gu.typesafe.config.impl.ConfigNodeComplexValue { - ConfigNodeObject(Collection children) { - super(children); - } - - @Override - protected ConfigNodeObject newNode(Collection nodes) { - return new ConfigNodeObject(nodes); - } - - public boolean hasValue(Path desiredPath) { - for (AbstractConfigNode node : children) { - if (node instanceof ConfigNodeField) { - ConfigNodeField field = (ConfigNodeField) node; - Path key = field.path().value(); - if (key.equals(desiredPath) || key.startsWith(desiredPath)) { - return true; - } else if (desiredPath.startsWith(key)) { - if (field.value() instanceof ConfigNodeObject) { - ConfigNodeObject obj = (ConfigNodeObject) field.value(); - Path remainingPath = desiredPath.subPath(key.length()); - if (obj.hasValue(remainingPath)) { - return true; - } - } - } - } - } - return false; - } - - protected ConfigNodeObject changeValueOnPath(Path desiredPath, com.gu.typesafe.config.impl.AbstractConfigNodeValue value, com.gu.typesafe.config.ConfigSyntax flavor) { - ArrayList childrenCopy = new ArrayList(super.children); - boolean seenNonMatching = false; - // Copy the value so we can change it to null but not modify the original parameter - com.gu.typesafe.config.impl.AbstractConfigNodeValue valueCopy = value; - for (int i = childrenCopy.size() - 1; i >= 0; i--) { - if (childrenCopy.get(i) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken) { - Token t = ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) childrenCopy.get(i)).token(); - // Ensure that, when we are removing settings in JSON, we don't end up with a trailing comma - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON && !seenNonMatching && t == com.gu.typesafe.config.impl.Tokens.COMMA) { - childrenCopy.remove(i); - } - continue; - } else if (!(childrenCopy.get(i) instanceof ConfigNodeField)) { - continue; - } - ConfigNodeField node = (ConfigNodeField) childrenCopy.get(i); - Path key = node.path().value(); - - // Delete all multi-element paths that start with the desired path, since technically they are duplicates - if ((valueCopy == null && key.equals(desiredPath))|| (key.startsWith(desiredPath) && !key.equals(desiredPath))) { - childrenCopy.remove(i); - // Remove any whitespace or commas after the deleted setting - for (int j = i; j < childrenCopy.size(); j++) { - if (childrenCopy.get(j) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken) { - Token t = ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) childrenCopy.get(j)).token(); - if (com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(t) || t == com.gu.typesafe.config.impl.Tokens.COMMA) { - childrenCopy.remove(j); - j--; - } else { - break; - } - } else { - break; - } - } - } else if (key.equals(desiredPath)) { - seenNonMatching = true; - com.gu.typesafe.config.impl.AbstractConfigNodeValue indentedValue; - AbstractConfigNode before = i - 1 > 0 ? childrenCopy.get(i - 1) : null; - if (value instanceof com.gu.typesafe.config.impl.ConfigNodeComplexValue && - before instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) before).token())) - indentedValue = ((com.gu.typesafe.config.impl.ConfigNodeComplexValue) value).indentText(before); - else - indentedValue = value; - childrenCopy.set(i, node.replaceValue(indentedValue)); - valueCopy = null; - } else if (desiredPath.startsWith(key)) { - seenNonMatching = true; - if (node.value() instanceof ConfigNodeObject) { - Path remainingPath = desiredPath.subPath(key.length()); - childrenCopy.set(i, node.replaceValue(((ConfigNodeObject) node.value()).changeValueOnPath(remainingPath, valueCopy, flavor))); - if (valueCopy != null && !node.equals(super.children.get(i))) - valueCopy = null; - } - } else { - seenNonMatching = true; - } - } - return new ConfigNodeObject(childrenCopy); - } - - public ConfigNodeObject setValueOnPath(String desiredPath, com.gu.typesafe.config.impl.AbstractConfigNodeValue value) { - return setValueOnPath(desiredPath, value, com.gu.typesafe.config.ConfigSyntax.CONF); - } - - public ConfigNodeObject setValueOnPath(String desiredPath, com.gu.typesafe.config.impl.AbstractConfigNodeValue value, com.gu.typesafe.config.ConfigSyntax flavor) { - com.gu.typesafe.config.impl.ConfigNodePath path = com.gu.typesafe.config.impl.PathParser.parsePathNode(desiredPath, flavor); - return setValueOnPath(path, value, flavor); - } - - private ConfigNodeObject setValueOnPath(com.gu.typesafe.config.impl.ConfigNodePath desiredPath, com.gu.typesafe.config.impl.AbstractConfigNodeValue value, com.gu.typesafe.config.ConfigSyntax flavor) { - ConfigNodeObject node = changeValueOnPath(desiredPath.value(), value, flavor); - - // If the desired Path did not exist, add it - if (!node.hasValue(desiredPath.value())) { - return node.addValueOnPath(desiredPath, value, flavor); - } - return node; - } - - private Collection indentation() { - boolean seenNewLine = false; - ArrayList indentation = new ArrayList(); - if (children.isEmpty()) { - return indentation; - } - for (int i = 0; i < children.size(); i++) { - if (!seenNewLine) { - if (children.get(i) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) children.get(i)).token())) { - seenNewLine = true; - indentation.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.newLine(null))); - } - } else { - if (children.get(i) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) children.get(i)).token()) && - i + 1 < children.size() && (children.get(i+1) instanceof ConfigNodeField || - children.get(i+1) instanceof ConfigNodeInclude)) { - // Return the indentation of the first setting on its own line - indentation.add(children.get(i)); - return indentation; - } - } - } - if (indentation.isEmpty()) { - indentation.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.newIgnoredWhitespace(null, " "))); - } else { - // Calculate the indentation of the ending curly-brace to get the indentation of the root object - AbstractConfigNode last = children.get(children.size() - 1); - if (last instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) last).token() == com.gu.typesafe.config.impl.Tokens.CLOSE_CURLY) { - AbstractConfigNode beforeLast = children.get(children.size() - 2); - String indent = ""; - if (beforeLast instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) beforeLast).token())) - indent = ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) beforeLast).token().tokenText(); - indent += " "; - indentation.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.newIgnoredWhitespace(null, indent))); - return indentation; - } - } - - // The object has no curly braces and is at the root level, so don't indent - return indentation; - } - - protected ConfigNodeObject addValueOnPath(com.gu.typesafe.config.impl.ConfigNodePath desiredPath, com.gu.typesafe.config.impl.AbstractConfigNodeValue value, com.gu.typesafe.config.ConfigSyntax flavor) { - Path path = desiredPath.value(); - ArrayList childrenCopy = new ArrayList(super.children); - ArrayList indentation = new ArrayList(indentation()); - - // If the value we're inserting is a complex value, we'll need to indent it for insertion - com.gu.typesafe.config.impl.AbstractConfigNodeValue indentedValue; - if (value instanceof com.gu.typesafe.config.impl.ConfigNodeComplexValue && !indentation.isEmpty()) { - indentedValue = ((com.gu.typesafe.config.impl.ConfigNodeComplexValue) value).indentText(indentation.get(indentation.size() - 1)); - } else { - indentedValue = value; - } - boolean sameLine = !(indentation.size() > 0 && indentation.get(0) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) indentation.get(0)).token())); - - // If the path is of length greater than one, see if the value needs to be added further down - if (path.length() > 1) { - for (int i = super.children.size() - 1; i >= 0; i--) { - if (!(super.children.get(i) instanceof ConfigNodeField)) { - continue; - } - ConfigNodeField node = (ConfigNodeField) super.children.get(i); - Path key = node.path().value(); - if (path.startsWith(key) && node.value() instanceof ConfigNodeObject) { - com.gu.typesafe.config.impl.ConfigNodePath remainingPath = desiredPath.subPath(key.length()); - ConfigNodeObject newValue = (ConfigNodeObject) node.value(); - childrenCopy.set(i, node.replaceValue(newValue.addValueOnPath(remainingPath, value, flavor))); - return new ConfigNodeObject(childrenCopy); - } - } - } - - // Otherwise, construct the new setting - boolean startsWithBrace = !super.children.isEmpty() && super.children.get(0) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) super.children.get(0)).token() == com.gu.typesafe.config.impl.Tokens.OPEN_CURLY; - ArrayList newNodes = new ArrayList(); - newNodes.addAll(indentation); - newNodes.add(desiredPath.first()); - newNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.newIgnoredWhitespace(null, " "))); - newNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.COLON)); - newNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.newIgnoredWhitespace(null, " "))); - - if (path.length() == 1) { - newNodes.add(indentedValue); - } else { - // If the path is of length greater than one add the required new objects along the path - ArrayList newObjectNodes = new ArrayList(); - newObjectNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.OPEN_CURLY)); - if (indentation.isEmpty()) { - newObjectNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.newLine(null))); - } - newObjectNodes.addAll(indentation); - newObjectNodes.add(new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.CLOSE_CURLY)); - ConfigNodeObject newObject = new ConfigNodeObject(newObjectNodes); - newNodes.add(newObject.addValueOnPath(desiredPath.subPath(1), indentedValue, flavor)); - } - - // Combine these two cases so that we only have to iterate once - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON || startsWithBrace || sameLine) { - for (int i = childrenCopy.size() - 1; i >= 0; i--) { - - // If we are in JSON or are adding a setting on the same line, we need to add a comma to the - // last setting - if ((flavor == com.gu.typesafe.config.ConfigSyntax.JSON || sameLine) && childrenCopy.get(i) instanceof ConfigNodeField) { - if (i+1 >= childrenCopy.size() || - !(childrenCopy.get(i+1) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken - && ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) childrenCopy.get(i+1)).token() == com.gu.typesafe.config.impl.Tokens.COMMA)) - childrenCopy.add(i+1, new com.gu.typesafe.config.impl.ConfigNodeSingleToken(com.gu.typesafe.config.impl.Tokens.COMMA)); - break; - } - - // Add the value into the copy of the children map, keeping any whitespace/newlines - // before the close curly brace - if (startsWithBrace && childrenCopy.get(i) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) childrenCopy.get(i)).token == com.gu.typesafe.config.impl.Tokens.CLOSE_CURLY) { - AbstractConfigNode previous = childrenCopy.get(i - 1); - if (previous instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) previous).token())) { - childrenCopy.add(i - 1, new ConfigNodeField(newNodes)); - i--; - } else if (previous instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) previous).token())) { - AbstractConfigNode beforePrevious = childrenCopy.get(i - 2); - if (sameLine) { - childrenCopy.add(i - 1, new ConfigNodeField(newNodes)); - i--; - } - else if (beforePrevious instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) beforePrevious).token())) { - childrenCopy.add(i - 2, new ConfigNodeField(newNodes)); - i -= 2; - } else { - childrenCopy.add(i, new ConfigNodeField(newNodes)); - } - - } - else - childrenCopy.add(i, new ConfigNodeField(newNodes)); - } - } - } - if (!startsWithBrace) { - if (!childrenCopy.isEmpty() && childrenCopy.get(childrenCopy.size() - 1) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && - com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) childrenCopy.get(childrenCopy.size() - 1)).token())) - childrenCopy.add(childrenCopy.size() - 1, new ConfigNodeField(newNodes)); - else - childrenCopy.add(new ConfigNodeField(newNodes)); - } - return new ConfigNodeObject(childrenCopy); - } - - public ConfigNodeObject removeValueOnPath(String desiredPath, ConfigSyntax flavor) { - Path path = com.gu.typesafe.config.impl.PathParser.parsePathNode(desiredPath, flavor).value(); - return changeValueOnPath(path, null, flavor); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodePath.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodePath.java deleted file mode 100644 index c0bc4a5349..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodePath.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigException; - -import java.util.ArrayList; -import java.util.Collection; - -final class ConfigNodePath extends AbstractConfigNode { - final private Path path; - final ArrayList tokens; - ConfigNodePath(Path path, Collection tokens) { - this.path = path; - this.tokens = new ArrayList(tokens); - } - - @Override - protected Collection tokens() { - return tokens; - } - - protected Path value() { - return path; - } - - protected ConfigNodePath subPath(int toRemove) { - int periodCount = 0; - ArrayList tokensCopy = new ArrayList(tokens); - for (int i = 0; i < tokensCopy.size(); i++) { - if (com.gu.typesafe.config.impl.Tokens.isUnquotedText(tokensCopy.get(i)) && - tokensCopy.get(i).tokenText().equals(".")) - periodCount++; - - if (periodCount == toRemove) { - return new ConfigNodePath(path.subPath(toRemove), tokensCopy.subList(i + 1, tokensCopy.size())); - } - } - throw new ConfigException.BugOrBroken("Tried to remove too many elements from a Path node"); - } - - protected ConfigNodePath first() { - ArrayList tokensCopy = new ArrayList(tokens); - for (int i = 0; i < tokensCopy.size(); i++) { - if (com.gu.typesafe.config.impl.Tokens.isUnquotedText(tokensCopy.get(i)) && - tokensCopy.get(i).tokenText().equals(".")) - return new ConfigNodePath(path.subPath(0, 1), tokensCopy.subList(0, i)); - } - return this; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeRoot.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeRoot.java deleted file mode 100644 index fa8c4c4529..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeRoot.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigSyntax; - -import java.util.ArrayList; -import java.util.Collection; - -final class ConfigNodeRoot extends com.gu.typesafe.config.impl.ConfigNodeComplexValue { - final private com.gu.typesafe.config.ConfigOrigin origin; - - ConfigNodeRoot(Collection children, ConfigOrigin origin) { - super(children); - this.origin = origin; - } - - @Override - protected ConfigNodeRoot newNode(Collection nodes) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Tried to indent the root object"); - } - - protected com.gu.typesafe.config.impl.ConfigNodeComplexValue value() { - for (com.gu.typesafe.config.impl.AbstractConfigNode node : children) { - if (node instanceof com.gu.typesafe.config.impl.ConfigNodeComplexValue) { - return (com.gu.typesafe.config.impl.ConfigNodeComplexValue)node; - } - } - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("ConfigNodeRoot did not contain a value"); - } - - protected ConfigNodeRoot setValue(String desiredPath, com.gu.typesafe.config.impl.AbstractConfigNodeValue value, ConfigSyntax flavor) { - ArrayList childrenCopy = new ArrayList(children); - for (int i = 0; i < childrenCopy.size(); i++) { - com.gu.typesafe.config.impl.AbstractConfigNode node = childrenCopy.get(i); - if (node instanceof com.gu.typesafe.config.impl.ConfigNodeComplexValue) { - if (node instanceof ConfigNodeArray) { - throw new com.gu.typesafe.config.ConfigException.WrongType(origin, "The ConfigDocument had an array at the root level, and values cannot be modified inside an array."); - } else if (node instanceof com.gu.typesafe.config.impl.ConfigNodeObject) { - if (value == null) { - childrenCopy.set(i, ((com.gu.typesafe.config.impl.ConfigNodeObject)node).removeValueOnPath(desiredPath, flavor)); - } else { - childrenCopy.set(i, ((com.gu.typesafe.config.impl.ConfigNodeObject) node).setValueOnPath(desiredPath, value, flavor)); - } - return new ConfigNodeRoot(childrenCopy, origin); - } - } - } - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("ConfigNodeRoot did not contain a value"); - } - - protected boolean hasValue(String desiredPath) { - Path path = com.gu.typesafe.config.impl.PathParser.parsePath(desiredPath); - ArrayList childrenCopy = new ArrayList(children); - for (int i = 0; i < childrenCopy.size(); i++) { - com.gu.typesafe.config.impl.AbstractConfigNode node = childrenCopy.get(i); - if (node instanceof com.gu.typesafe.config.impl.ConfigNodeComplexValue) { - if (node instanceof ConfigNodeArray) { - throw new com.gu.typesafe.config.ConfigException.WrongType(origin, "The ConfigDocument had an array at the root level, and values cannot be modified inside an array."); - } else if (node instanceof com.gu.typesafe.config.impl.ConfigNodeObject) { - return ((com.gu.typesafe.config.impl.ConfigNodeObject) node).hasValue(path); - } - } - } - throw new ConfigException.BugOrBroken("ConfigNodeRoot did not contain a value"); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeSimpleValue.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeSimpleValue.java deleted file mode 100644 index 63d3f7b08c..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeSimpleValue.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigException; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -final class ConfigNodeSimpleValue extends AbstractConfigNodeValue { - final Token token; - ConfigNodeSimpleValue(Token value) { - token = value; - } - - @Override - protected Collection tokens() { - return Collections.singletonList(token); - } - - protected Token token() { return token; } - - protected com.gu.typesafe.config.impl.AbstractConfigValue value() { - if (Tokens.isValue(token)) - return Tokens.getValue(token); - else if (Tokens.isUnquotedText(token)) - return new ConfigString.Unquoted(token.origin(), Tokens.getUnquotedText(token)); - else if (Tokens.isSubstitution(token)) { - List expression = Tokens.getSubstitutionPathExpression(token); - Path path = PathParser.parsePathExpression(expression.iterator(), token.origin()); - boolean optional = Tokens.getSubstitutionOptional(token); - - return new ConfigReference(token.origin(), new SubstitutionExpression(path, optional)); - } - throw new ConfigException.BugOrBroken("ConfigNodeSimpleValue did not contain a valid value token"); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeSingleToken.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeSingleToken.java deleted file mode 100644 index 442006a1d7..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNodeSingleToken.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.Collection; -import java.util.Collections; - -class ConfigNodeSingleToken extends AbstractConfigNode { - final Token token; - ConfigNodeSingleToken(Token t) { - token = t; - } - - @Override - protected Collection tokens() { - return Collections.singletonList(token); - } - - protected Token token() { return token; } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNull.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNull.java deleted file mode 100644 index 607c4de844..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNull.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.ObjectStreamException; -import java.io.Serializable; - -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigRenderOptions; -import com.gu.typesafe.config.ConfigValueType; - -/** - * This exists because sometimes null is not the same as missing. Specifically, - * if a value is set to null we can give a better error message (indicating - * where it was set to null) in case someone asks for the value. Also, null - * overrides values set "earlier" in the search path, while missing values do - * not. - * - */ -final class ConfigNull extends com.gu.typesafe.config.impl.AbstractConfigValue implements Serializable { - - private static final long serialVersionUID = 2L; - - ConfigNull(com.gu.typesafe.config.ConfigOrigin origin) { - super(origin); - } - - @Override - public com.gu.typesafe.config.ConfigValueType valueType() { - return ConfigValueType.NULL; - } - - @Override - public Object unwrapped() { - return null; - } - - @Override - String transformToString() { - return "null"; - } - - @Override - protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) { - sb.append("null"); - } - - @Override - protected ConfigNull newCopy(ConfigOrigin origin) { - return new ConfigNull(origin); - } - - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new com.gu.typesafe.config.impl.SerializedConfigValue(this); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNumber.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNumber.java deleted file mode 100644 index c16d33e10b..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigNumber.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.ObjectStreamException; -import java.io.Serializable; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; - -abstract class ConfigNumber extends com.gu.typesafe.config.impl.AbstractConfigValue implements Serializable { - - private static final long serialVersionUID = 2L; - - // This is so when we concatenate a number into a string (say it appears in - // a sentence) we always have it exactly as the person typed it into the - // config file. It's purely cosmetic; equals/hashCode don't consider this - // for example. - final protected String originalText; - - protected ConfigNumber(com.gu.typesafe.config.ConfigOrigin origin, String originalText) { - super(origin); - this.originalText = originalText; - } - - @Override - public abstract Number unwrapped(); - - @Override - String transformToString() { - return originalText; - } - - int intValueRangeChecked(String path) { - long l = longValue(); - if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) { - throw new ConfigException.WrongType(origin(), path, "32-bit integer", - "out-of-range value " + l); - } - return (int) l; - } - - protected abstract long longValue(); - - protected abstract double doubleValue(); - - private boolean isWhole() { - long asLong = longValue(); - return asLong == doubleValue(); - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof ConfigNumber; - } - - @Override - public boolean equals(Object other) { - // note that "origin" is deliberately NOT part of equality - if (other instanceof ConfigNumber && canEqual(other)) { - ConfigNumber n = (ConfigNumber) other; - if (isWhole()) { - return n.isWhole() && this.longValue() == n.longValue(); - } else { - return (!n.isWhole()) && this.doubleValue() == n.doubleValue(); - } - } else { - return false; - } - } - - @Override - public int hashCode() { - // note that "origin" is deliberately NOT part of equality - - // this matches what standard Long.hashCode and Double.hashCode - // do, though I don't think it really matters. - long asLong; - if (isWhole()) { - asLong = longValue(); - } else { - asLong = Double.doubleToLongBits(doubleValue()); - } - return (int) (asLong ^ (asLong >>> 32)); - } - - static ConfigNumber newNumber(com.gu.typesafe.config.ConfigOrigin origin, long number, - String originalText) { - if (number <= Integer.MAX_VALUE && number >= Integer.MIN_VALUE) - return new com.gu.typesafe.config.impl.ConfigInt(origin, (int) number, originalText); - else - return new ConfigLong(origin, number, originalText); - } - - static ConfigNumber newNumber(ConfigOrigin origin, double number, - String originalText) { - long asLong = (long) number; - if (asLong == number) { - return newNumber(origin, asLong, originalText); - } else { - return new com.gu.typesafe.config.impl.ConfigDouble(origin, number, originalText); - } - } - - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new com.gu.typesafe.config.impl.SerializedConfigValue(this); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigParser.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigParser.java deleted file mode 100644 index 3a12fdec7b..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigParser.java +++ /dev/null @@ -1,422 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; - -import com.gu.typesafe.config.ConfigException; - -final class ConfigParser { - static com.gu.typesafe.config.impl.AbstractConfigValue parse(com.gu.typesafe.config.impl.ConfigNodeRoot document, - com.gu.typesafe.config.ConfigOrigin origin, com.gu.typesafe.config.ConfigParseOptions options, - com.gu.typesafe.config.ConfigIncludeContext includeContext) { - ParseContext context = new ParseContext(options.getSyntax(), origin, document, - com.gu.typesafe.config.impl.SimpleIncluder.makeFull(options.getIncluder()), includeContext); - return context.parse(); - } - - static private final class ParseContext { - private int lineNumber; - final private com.gu.typesafe.config.impl.ConfigNodeRoot document; - final private com.gu.typesafe.config.impl.FullIncluder includer; - final private com.gu.typesafe.config.ConfigIncludeContext includeContext; - final private com.gu.typesafe.config.ConfigSyntax flavor; - final private com.gu.typesafe.config.ConfigOrigin baseOrigin; - final private LinkedList pathStack; - - // the number of lists we are inside; this is used to detect the "cannot - // generate a reference to a list element" problem, and once we fix that - // problem we should be able to get rid of this variable. - int arrayCount; - - ParseContext(com.gu.typesafe.config.ConfigSyntax flavor, com.gu.typesafe.config.ConfigOrigin origin, com.gu.typesafe.config.impl.ConfigNodeRoot document, - com.gu.typesafe.config.impl.FullIncluder includer, com.gu.typesafe.config.ConfigIncludeContext includeContext) { - lineNumber = 1; - this.document = document; - this.flavor = flavor; - this.baseOrigin = origin; - this.includer = includer; - this.includeContext = includeContext; - this.pathStack = new LinkedList(); - this.arrayCount = 0; - } - - // merge a bunch of adjacent values into one - // value; change unquoted text into a string - // value. - private com.gu.typesafe.config.impl.AbstractConfigValue parseConcatenation(com.gu.typesafe.config.impl.ConfigNodeConcatenation n) { - // this trick is not done in JSON - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Found a concatenation node in JSON"); - - List values = new ArrayList(n.children().size()); - - for (com.gu.typesafe.config.impl.AbstractConfigNode node : n.children()) { - com.gu.typesafe.config.impl.AbstractConfigValue v = null; - if (node instanceof com.gu.typesafe.config.impl.AbstractConfigNodeValue) { - v = parseValue((com.gu.typesafe.config.impl.AbstractConfigNodeValue)node, null); - values.add(v); - } - } - - return com.gu.typesafe.config.impl.ConfigConcatenation.concatenate(values); - } - - private com.gu.typesafe.config.impl.SimpleConfigOrigin lineOrigin() { - return ((com.gu.typesafe.config.impl.SimpleConfigOrigin) baseOrigin).withLineNumber(lineNumber); - } - - private com.gu.typesafe.config.ConfigException parseError(String message) { - return parseError(message, null); - } - - private com.gu.typesafe.config.ConfigException parseError(String message, Throwable cause) { - return new com.gu.typesafe.config.ConfigException.Parse(lineOrigin(), message, cause); - } - - private Path fullCurrentPath() { - // pathStack has top of stack at front - if (pathStack.isEmpty()) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Bug in parser; tried to get current path when at root"); - else - return new Path(pathStack.descendingIterator()); - } - - private com.gu.typesafe.config.impl.AbstractConfigValue parseValue(com.gu.typesafe.config.impl.AbstractConfigNodeValue n, List comments) { - com.gu.typesafe.config.impl.AbstractConfigValue v; - - int startingArrayCount = arrayCount; - - if (n instanceof com.gu.typesafe.config.impl.ConfigNodeSimpleValue) { - v = ((com.gu.typesafe.config.impl.ConfigNodeSimpleValue) n).value(); - } else if (n instanceof com.gu.typesafe.config.impl.ConfigNodeObject) { - v = parseObject((com.gu.typesafe.config.impl.ConfigNodeObject)n); - } else if (n instanceof com.gu.typesafe.config.impl.ConfigNodeArray) { - v = parseArray((com.gu.typesafe.config.impl.ConfigNodeArray)n); - } else if (n instanceof com.gu.typesafe.config.impl.ConfigNodeConcatenation) { - v = parseConcatenation((com.gu.typesafe.config.impl.ConfigNodeConcatenation)n); - } else { - throw parseError("Expecting a value but got wrong node type: " + n.getClass()); - } - - if (comments != null && !comments.isEmpty()) { - v = v.withOrigin(v.origin().prependComments(new ArrayList(comments))); - comments.clear(); - } - - if (arrayCount != startingArrayCount) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Bug in config parser: unbalanced array count"); - - return v; - } - - private static com.gu.typesafe.config.impl.AbstractConfigObject createValueUnderPath(Path path, - com.gu.typesafe.config.impl.AbstractConfigValue value) { - // for path foo.bar, we are creating - // { "foo" : { "bar" : value } } - List keys = new ArrayList(); - - String key = path.first(); - Path remaining = path.remainder(); - while (key != null) { - keys.add(key); - if (remaining == null) { - break; - } else { - key = remaining.first(); - remaining = remaining.remainder(); - } - } - - // the withComments(null) is to ensure comments are only - // on the exact leaf node they apply to. - // a comment before "foo.bar" applies to the full setting - // "foo.bar" not also to "foo" - ListIterator i = keys.listIterator(keys.size()); - String deepest = i.previous(); - com.gu.typesafe.config.impl.AbstractConfigObject o = new com.gu.typesafe.config.impl.SimpleConfigObject(value.origin().withComments(null), - Collections. singletonMap( - deepest, value)); - while (i.hasPrevious()) { - Map m = Collections. singletonMap( - i.previous(), o); - o = new com.gu.typesafe.config.impl.SimpleConfigObject(value.origin().withComments(null), m); - } - - return o; - } - - private void parseInclude(Map values, com.gu.typesafe.config.impl.ConfigNodeInclude n) { - boolean isRequired = n.isRequired(); - com.gu.typesafe.config.ConfigIncludeContext cic = includeContext.setParseOptions(includeContext.parseOptions().setAllowMissing(!isRequired)); - - com.gu.typesafe.config.impl.AbstractConfigObject obj; - switch (n.kind()) { - case URL: - URL url; - try { - url = new URL(n.name()); - } catch (MalformedURLException e) { - throw parseError("include url() specifies an invalid URL: " + n.name(), e); - } - obj = (com.gu.typesafe.config.impl.AbstractConfigObject) includer.includeURL(cic, url); - break; - - case FILE: - obj = (com.gu.typesafe.config.impl.AbstractConfigObject) includer.includeFile(cic, - new File(n.name())); - break; - - case CLASSPATH: - obj = (com.gu.typesafe.config.impl.AbstractConfigObject) includer.includeResources(cic, n.name()); - break; - - case HEURISTIC: - obj = (com.gu.typesafe.config.impl.AbstractConfigObject) includer - .include(cic, n.name()); - break; - - default: - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("should not be reached"); - } - - // we really should make this work, but for now throwing an - // exception is better than producing an incorrect result. - // See https://github.com/lightbend/config/issues/160 - if (arrayCount > 0 && obj.resolveStatus() != ResolveStatus.RESOLVED) - throw parseError("Due to current limitations of the config parser, when an include statement is nested inside a list value, " - + "${} substitutions inside the included file cannot be resolved correctly. Either move the include outside of the list value or " - + "remove the ${} statements from the included file."); - - if (!pathStack.isEmpty()) { - Path prefix = fullCurrentPath(); - obj = obj.relativized(prefix); - } - - for (String key : obj.keySet()) { - com.gu.typesafe.config.impl.AbstractConfigValue v = obj.get(key); - com.gu.typesafe.config.impl.AbstractConfigValue existing = values.get(key); - if (existing != null) { - values.put(key, v.withFallback(existing)); - } else { - values.put(key, v); - } - } - } - - private com.gu.typesafe.config.impl.AbstractConfigObject parseObject(com.gu.typesafe.config.impl.ConfigNodeObject n) { - Map values = new HashMap(); - com.gu.typesafe.config.impl.SimpleConfigOrigin objectOrigin = lineOrigin(); - boolean lastWasNewline = false; - - ArrayList nodes = new ArrayList(n.children()); - List comments = new ArrayList(); - for (int i = 0; i < nodes.size(); i++) { - com.gu.typesafe.config.impl.AbstractConfigNode node = nodes.get(i); - if (node instanceof com.gu.typesafe.config.impl.ConfigNodeComment) { - lastWasNewline = false; - comments.add(((com.gu.typesafe.config.impl.ConfigNodeComment) node).commentText()); - } else if (node instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) node).token())) { - lineNumber++; - if (lastWasNewline) { - // Drop all comments if there was a blank line and start a new comment block - comments.clear(); - } - lastWasNewline = true; - } else if (flavor != com.gu.typesafe.config.ConfigSyntax.JSON && node instanceof com.gu.typesafe.config.impl.ConfigNodeInclude) { - parseInclude(values, (com.gu.typesafe.config.impl.ConfigNodeInclude)node); - lastWasNewline = false; - } else if (node instanceof com.gu.typesafe.config.impl.ConfigNodeField) { - lastWasNewline = false; - Path path = ((com.gu.typesafe.config.impl.ConfigNodeField) node).path().value(); - comments.addAll(((com.gu.typesafe.config.impl.ConfigNodeField) node).comments()); - - // path must be on-stack while we parse the value - pathStack.push(path); - if (((com.gu.typesafe.config.impl.ConfigNodeField) node).separator() == com.gu.typesafe.config.impl.Tokens.PLUS_EQUALS) { - // we really should make this work, but for now throwing - // an exception is better than producing an incorrect - // result. See - // https://github.com/lightbend/config/issues/160 - if (arrayCount > 0) - throw parseError("Due to current limitations of the config parser, += does not work nested inside a list. " - + "+= expands to a ${} substitution and the path in ${} cannot currently refer to list elements. " - + "You might be able to move the += outside of the list and then refer to it from inside the list with ${}."); - - // because we will put it in an array after the fact so - // we want this to be incremented during the parseValue - // below in order to throw the above exception. - arrayCount += 1; - } - - com.gu.typesafe.config.impl.AbstractConfigNodeValue valueNode; - com.gu.typesafe.config.impl.AbstractConfigValue newValue; - - valueNode = ((com.gu.typesafe.config.impl.ConfigNodeField) node).value(); - - // comments from the key token go to the value token - newValue = parseValue(valueNode, comments); - - if (((com.gu.typesafe.config.impl.ConfigNodeField) node).separator() == com.gu.typesafe.config.impl.Tokens.PLUS_EQUALS) { - arrayCount -= 1; - - List concat = new ArrayList(2); - com.gu.typesafe.config.impl.AbstractConfigValue previousRef = new com.gu.typesafe.config.impl.ConfigReference(newValue.origin(), - new com.gu.typesafe.config.impl.SubstitutionExpression(fullCurrentPath(), true /* optional */)); - com.gu.typesafe.config.impl.AbstractConfigValue list = new com.gu.typesafe.config.impl.SimpleConfigList(newValue.origin(), - Collections.singletonList(newValue)); - concat.add(previousRef); - concat.add(list); - newValue = com.gu.typesafe.config.impl.ConfigConcatenation.concatenate(concat); - } - - // Grab any trailing comments on the same line - if (i < nodes.size() - 1) { - i++; - while (i < nodes.size()) { - if (nodes.get(i) instanceof com.gu.typesafe.config.impl.ConfigNodeComment) { - com.gu.typesafe.config.impl.ConfigNodeComment comment = (com.gu.typesafe.config.impl.ConfigNodeComment) nodes.get(i); - newValue = newValue.withOrigin(newValue.origin().appendComments( - Collections.singletonList(comment.commentText()))); - break; - } else if (nodes.get(i) instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken) { - com.gu.typesafe.config.impl.ConfigNodeSingleToken curr = (com.gu.typesafe.config.impl.ConfigNodeSingleToken) nodes.get(i); - if (curr.token() == com.gu.typesafe.config.impl.Tokens.COMMA || com.gu.typesafe.config.impl.Tokens.isIgnoredWhitespace(curr.token())) { - // keep searching, as there could still be a comment - } else { - i--; - break; - } - } else { - i--; - break; - } - i++; - } - } - - pathStack.pop(); - - String key = path.first(); - Path remaining = path.remainder(); - - if (remaining == null) { - com.gu.typesafe.config.impl.AbstractConfigValue existing = values.get(key); - if (existing != null) { - // In strict JSON, dups should be an error; while in - // our custom config language, they should be merged - // if the value is an object (or substitution that - // could become an object). - - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) { - throw parseError("JSON does not allow duplicate fields: '" - + key - + "' was already seen at " - + existing.origin().description()); - } else { - newValue = newValue.withFallback(existing); - } - } - values.put(key, newValue); - } else { - if (flavor == com.gu.typesafe.config.ConfigSyntax.JSON) { - throw new ConfigException.BugOrBroken( - "somehow got multi-element path in JSON mode"); - } - - com.gu.typesafe.config.impl.AbstractConfigObject obj = createValueUnderPath( - remaining, newValue); - com.gu.typesafe.config.impl.AbstractConfigValue existing = values.get(key); - if (existing != null) { - obj = obj.withFallback(existing); - } - values.put(key, obj); - } - } - } - - return new com.gu.typesafe.config.impl.SimpleConfigObject(objectOrigin, values); - } - - private com.gu.typesafe.config.impl.SimpleConfigList parseArray(com.gu.typesafe.config.impl.ConfigNodeArray n) { - arrayCount += 1; - - com.gu.typesafe.config.impl.SimpleConfigOrigin arrayOrigin = lineOrigin(); - List values = new ArrayList(); - - boolean lastWasNewLine = false; - List comments = new ArrayList(); - - com.gu.typesafe.config.impl.AbstractConfigValue v = null; - - for (com.gu.typesafe.config.impl.AbstractConfigNode node : n.children()) { - if (node instanceof com.gu.typesafe.config.impl.ConfigNodeComment) { - comments.add(((com.gu.typesafe.config.impl.ConfigNodeComment) node).commentText()); - lastWasNewLine = false; - } else if (node instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken && com.gu.typesafe.config.impl.Tokens.isNewline(((com.gu.typesafe.config.impl.ConfigNodeSingleToken) node).token())) { - lineNumber++; - if (lastWasNewLine && v == null) { - comments.clear(); - } else if (v != null) { - values.add(v.withOrigin(v.origin().appendComments(new ArrayList(comments)))); - comments.clear(); - v = null; - } - lastWasNewLine = true; - } else if (node instanceof com.gu.typesafe.config.impl.AbstractConfigNodeValue) { - lastWasNewLine = false; - if (v != null) { - values.add(v.withOrigin(v.origin().appendComments(new ArrayList(comments)))); - comments.clear(); - } - v = parseValue((com.gu.typesafe.config.impl.AbstractConfigNodeValue)node, comments); - } - } - // There shouldn't be any comments at this point, but add them just in case - if (v != null) { - values.add(v.withOrigin(v.origin().appendComments(new ArrayList(comments)))); - } - arrayCount -= 1; - return new com.gu.typesafe.config.impl.SimpleConfigList(arrayOrigin, values); - } - - com.gu.typesafe.config.impl.AbstractConfigValue parse() { - com.gu.typesafe.config.impl.AbstractConfigValue result = null; - ArrayList comments = new ArrayList(); - boolean lastWasNewLine = false; - for (com.gu.typesafe.config.impl.AbstractConfigNode node : document.children()) { - if (node instanceof com.gu.typesafe.config.impl.ConfigNodeComment) { - comments.add(((com.gu.typesafe.config.impl.ConfigNodeComment) node).commentText()); - lastWasNewLine = false; - } else if (node instanceof com.gu.typesafe.config.impl.ConfigNodeSingleToken) { - com.gu.typesafe.config.impl.Token t = ((com.gu.typesafe.config.impl.ConfigNodeSingleToken) node).token(); - if (com.gu.typesafe.config.impl.Tokens.isNewline(t)) { - lineNumber++; - if (lastWasNewLine && result == null) { - comments.clear(); - } else if (result != null) { - result = result.withOrigin(result.origin().appendComments(new ArrayList(comments))); - comments.clear(); - break; - } - lastWasNewLine = true; - } - } else if (node instanceof com.gu.typesafe.config.impl.ConfigNodeComplexValue) { - result = parseValue((com.gu.typesafe.config.impl.ConfigNodeComplexValue)node, comments); - lastWasNewLine = false; - } - } - return result; - } - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigReference.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigReference.java deleted file mode 100644 index 5aadabe85e..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigReference.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.gu.typesafe.config.impl; - -import java.util.Collection; -import java.util.Collections; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigValue; -import com.gu.typesafe.config.ConfigValueType; -import com.gu.typesafe.config.ConfigRenderOptions; - -/** - * ConfigReference replaces ConfigReference (the older class kept for back - * compat) and represents the ${} substitution syntax. It can resolve to any - * kind of value. - */ -final class ConfigReference extends com.gu.typesafe.config.impl.AbstractConfigValue implements com.gu.typesafe.config.impl.Unmergeable { - - final private com.gu.typesafe.config.impl.SubstitutionExpression expr; - // the length of any prefixes added with relativized() - final private int prefixLength; - - ConfigReference(com.gu.typesafe.config.ConfigOrigin origin, com.gu.typesafe.config.impl.SubstitutionExpression expr) { - this(origin, expr, 0); - } - - private ConfigReference(com.gu.typesafe.config.ConfigOrigin origin, com.gu.typesafe.config.impl.SubstitutionExpression expr, int prefixLength) { - super(origin); - this.expr = expr; - this.prefixLength = prefixLength; - } - - private com.gu.typesafe.config.ConfigException.NotResolved notResolved() { - return new com.gu.typesafe.config.ConfigException.NotResolved( - "need to Config#resolve(), see the API docs for Config#resolve(); substitution not resolved: " - + this); - } - - @Override - public ConfigValueType valueType() { - throw notResolved(); - } - - @Override - public Object unwrapped() { - throw notResolved(); - } - - @Override - protected ConfigReference newCopy(ConfigOrigin newOrigin) { - return new ConfigReference(newOrigin, expr, prefixLength); - } - - @Override - protected boolean ignoresFallbacks() { - return false; - } - - @Override - public Collection unmergedValues() { - return Collections.singleton(this); - } - - // ConfigReference should be a firewall against NotPossibleToResolve going - // further up the stack; it should convert everything to ConfigException. - // This way it's impossible for NotPossibleToResolve to "escape" since - // any failure to resolve has to start with a ConfigReference. - @Override - com.gu.typesafe.config.impl.ResolveResult resolveSubstitutions(com.gu.typesafe.config.impl.ResolveContext context, com.gu.typesafe.config.impl.ResolveSource source) { - com.gu.typesafe.config.impl.ResolveContext newContext = context.addCycleMarker(this); - com.gu.typesafe.config.impl.AbstractConfigValue v; - try { - com.gu.typesafe.config.impl.ResolveSource.ResultWithPath resultWithPath = source.lookupSubst(newContext, expr, prefixLength); - newContext = resultWithPath.result.context; - - if (resultWithPath.result.value != null) { - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - com.gu.typesafe.config.impl.ConfigImpl.trace(newContext.depth(), "recursively resolving " + resultWithPath - + " which was the resolution of " + expr + " against " + source); - - com.gu.typesafe.config.impl.ResolveSource recursiveResolveSource = (new com.gu.typesafe.config.impl.ResolveSource( - (AbstractConfigObject) resultWithPath.pathFromRoot.last(), resultWithPath.pathFromRoot)); - - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - com.gu.typesafe.config.impl.ConfigImpl.trace(newContext.depth(), "will recursively resolve against " + recursiveResolveSource); - - com.gu.typesafe.config.impl.ResolveResult result = newContext.resolve(resultWithPath.result.value, - recursiveResolveSource); - v = result.value; - newContext = result.context; - } else { - ConfigValue fallback = context.options().getResolver().lookup(expr.path().render()); - v = (com.gu.typesafe.config.impl.AbstractConfigValue) fallback; - } - } catch (NotPossibleToResolve e) { - if (com.gu.typesafe.config.impl.ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(newContext.depth(), - "not possible to resolve " + expr + ", cycle involved: " + e.traceString()); - if (expr.optional()) - v = null; - else - throw new com.gu.typesafe.config.ConfigException.UnresolvedSubstitution(origin(), expr - + " was part of a cycle of substitutions involving " + e.traceString(), e); - } - - if (v == null && !expr.optional()) { - if (newContext.options().getAllowUnresolved()) - return com.gu.typesafe.config.impl.ResolveResult.make(newContext.removeCycleMarker(this), this); - else - throw new ConfigException.UnresolvedSubstitution(origin(), expr.toString()); - } else { - return com.gu.typesafe.config.impl.ResolveResult.make(newContext.removeCycleMarker(this), v); - } - } - - @Override - ResolveStatus resolveStatus() { - return ResolveStatus.UNRESOLVED; - } - - // when you graft a substitution into another object, - // you have to prefix it with the location in that object - // where you grafted it; but save prefixLength so - // system property and env variable lookups don't get - // broken. - @Override - ConfigReference relativized(Path prefix) { - com.gu.typesafe.config.impl.SubstitutionExpression newExpr = expr.changePath(expr.path().prepend(prefix)); - return new ConfigReference(origin(), newExpr, prefixLength + prefix.length()); - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof ConfigReference; - } - - @Override - public boolean equals(Object other) { - // note that "origin" is deliberately NOT part of equality - if (other instanceof ConfigReference) { - return canEqual(other) && this.expr.equals(((ConfigReference) other).expr); - } else { - return false; - } - } - - @Override - public int hashCode() { - // note that "origin" is deliberately NOT part of equality - return expr.hashCode(); - } - - @Override - protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) { - sb.append(expr.toString()); - } - - com.gu.typesafe.config.impl.SubstitutionExpression expression() { - return expr; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigString.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigString.java deleted file mode 100644 index 5eb3d55cf1..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ConfigString.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.ObjectStreamException; -import java.io.Serializable; - -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigValueType; -import com.gu.typesafe.config.ConfigRenderOptions; - -abstract class ConfigString extends com.gu.typesafe.config.impl.AbstractConfigValue implements Serializable { - - private static final long serialVersionUID = 2L; - - final protected String value; - - protected ConfigString(com.gu.typesafe.config.ConfigOrigin origin, String value) { - super(origin); - this.value = value; - } - - - final static class Quoted extends ConfigString { - Quoted(com.gu.typesafe.config.ConfigOrigin origin, String value) { - super(origin, value); - } - @Override - protected Quoted newCopy(com.gu.typesafe.config.ConfigOrigin origin) { - return new Quoted(origin, value); - } - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new com.gu.typesafe.config.impl.SerializedConfigValue(this); - } - } - - // this is sort of a hack; we want to preserve whether whitespace - // was quoted until we process substitutions, so we can ignore - // unquoted whitespace when concatenating lists or objects. - // We dump this distinction when serializing and deserializing, - // but that's OK because it isn't in equals/hashCode, and we - // don't allow serializing unresolved objects which is where - // quoted-ness matters. If we later make ConfigOrigin point - // to the original token range, we could use that to implement - // wasQuoted() - final static class Unquoted extends ConfigString { - Unquoted(com.gu.typesafe.config.ConfigOrigin origin, String value) { - super(origin, value); - } - @Override - protected Unquoted newCopy(ConfigOrigin origin) { - return new Unquoted(origin, value); - } - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new com.gu.typesafe.config.impl.SerializedConfigValue(this); - } - } - - boolean wasQuoted() { - return (this instanceof Quoted); - } - - @Override - public com.gu.typesafe.config.ConfigValueType valueType() { - return ConfigValueType.STRING; - } - - @Override - public String unwrapped() { - return value; - } - - @Override - String transformToString() { - return value; - } - - @Override - protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) { - String rendered; - if (options.getJson()) - rendered = ConfigImplUtil.renderJsonString(value); - else - rendered = ConfigImplUtil.renderStringUnquotedIfPossible(value); - sb.append(rendered); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/Container.java b/scripts/src/main/java/com/gu/typesafe/config/impl/Container.java deleted file mode 100644 index 78fc049004..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/Container.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (C) 2014 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigValue; - -/** - * An AbstractConfigValue which contains other values. Java has no way to - * express "this has to be an AbstractConfigValue also" other than making - * AbstractConfigValue an interface which would be aggravating. But we can say - * we are a ConfigValue. - */ -interface Container extends ConfigValue { - /** - * Replace a child of this value. CAUTION if replacement is null, delete the - * child, which may also delete the parent, or make the parent into a - * non-container. - */ - com.gu.typesafe.config.impl.AbstractConfigValue replaceChild(com.gu.typesafe.config.impl.AbstractConfigValue child, com.gu.typesafe.config.impl.AbstractConfigValue replacement); - - /** - * Super-expensive full traversal to see if descendant is anywhere - * underneath this container. - */ - boolean hasDescendant(com.gu.typesafe.config.impl.AbstractConfigValue descendant); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/DefaultTransformer.java b/scripts/src/main/java/com/gu/typesafe/config/impl/DefaultTransformer.java deleted file mode 100644 index 8845513c63..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/DefaultTransformer.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; - -import com.gu.typesafe.config.ConfigValueType; - -/** - * Default automatic type transformations. - */ -final class DefaultTransformer { - - static com.gu.typesafe.config.impl.AbstractConfigValue transform(com.gu.typesafe.config.impl.AbstractConfigValue value, - com.gu.typesafe.config.ConfigValueType requested) { - if (value.valueType() == com.gu.typesafe.config.ConfigValueType.STRING) { - String s = (String) value.unwrapped(); - switch (requested) { - case NUMBER: - try { - Long v = Long.parseLong(s); - return new com.gu.typesafe.config.impl.ConfigLong(value.origin(), v, s); - } catch (NumberFormatException e) { - // try Double - } - try { - Double v = Double.parseDouble(s); - return new com.gu.typesafe.config.impl.ConfigDouble(value.origin(), v, s); - } catch (NumberFormatException e) { - // oh well. - } - break; - case NULL: - if (s.equals("null")) - return new com.gu.typesafe.config.impl.ConfigNull(value.origin()); - break; - case BOOLEAN: - if (s.equals("true") || s.equals("yes") || s.equals("on")) { - return new com.gu.typesafe.config.impl.ConfigBoolean(value.origin(), true); - } else if (s.equals("false") || s.equals("no") - || s.equals("off")) { - return new com.gu.typesafe.config.impl.ConfigBoolean(value.origin(), false); - } - break; - case LIST: - // can't go STRING to LIST automatically - break; - case OBJECT: - // can't go STRING to OBJECT automatically - break; - case STRING: - // no-op STRING to STRING - break; - } - } else if (requested == com.gu.typesafe.config.ConfigValueType.STRING) { - // if we converted null to string here, then you wouldn't properly - // get a missing-value error if you tried to get a null value - // as a string. - switch (value.valueType()) { - case NUMBER: // FALL THROUGH - case BOOLEAN: - return new com.gu.typesafe.config.impl.ConfigString.Quoted(value.origin(), - value.transformToString()); - case NULL: - // want to be sure this throws instead of returning "null" as a - // string - break; - case OBJECT: - // no OBJECT to STRING automatically - break; - case LIST: - // no LIST to STRING automatically - break; - case STRING: - // no-op STRING to STRING - break; - } - } else if (requested == com.gu.typesafe.config.ConfigValueType.LIST && value.valueType() == ConfigValueType.OBJECT) { - // attempt to convert an array-like (numeric indices) object to a - // list. This would be used with .properties syntax for example: - // -Dfoo.0=bar -Dfoo.1=baz - // To ensure we still throw type errors for objects treated - // as lists in most cases, we'll refuse to convert if the object - // does not contain any numeric keys. This means we don't allow - // empty objects here though :-/ - AbstractConfigObject o = (AbstractConfigObject) value; - Map values = new HashMap(); - for (String key : o.keySet()) { - int i; - try { - i = Integer.parseInt(key, 10); - if (i < 0) - continue; - values.put(i, o.get(key)); - } catch (NumberFormatException e) { - continue; - } - } - if (!values.isEmpty()) { - ArrayList> entryList = new ArrayList>( - values.entrySet()); - // sort by numeric index - Collections.sort(entryList, - new Comparator>() { - @Override - public int compare(Map.Entry a, - Map.Entry b) { - return Integer.compare(a.getKey(), b.getKey()); - } - }); - // drop the indices (we allow gaps in the indices, for better or - // worse) - ArrayList list = new ArrayList(); - for (Map.Entry entry : entryList) { - list.add(entry.getValue()); - } - return new com.gu.typesafe.config.impl.SimpleConfigList(value.origin(), list); - } - } - - return value; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/FromMapMode.java b/scripts/src/main/java/com/gu/typesafe/config/impl/FromMapMode.java deleted file mode 100644 index 83036c184c..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/FromMapMode.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -enum FromMapMode { - KEYS_ARE_PATHS, KEYS_ARE_KEYS -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/FullIncluder.java b/scripts/src/main/java/com/gu/typesafe/config/impl/FullIncluder.java deleted file mode 100644 index d4c3c80d74..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/FullIncluder.java +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigIncluder; -import com.gu.typesafe.config.ConfigIncluderFile; -import com.gu.typesafe.config.ConfigIncluderURL; -import com.gu.typesafe.config.ConfigIncluderClasspath; - -interface FullIncluder extends ConfigIncluder, ConfigIncluderFile, ConfigIncluderURL, - ConfigIncluderClasspath { - -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/MemoKey.java b/scripts/src/main/java/com/gu/typesafe/config/impl/MemoKey.java deleted file mode 100644 index b9068b5918..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/MemoKey.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.gu.typesafe.config.impl; - -/** The key used to memoize already-traversed nodes when resolving substitutions */ -final class MemoKey { - MemoKey(com.gu.typesafe.config.impl.AbstractConfigValue value, Path restrictToChildOrNull) { - this.value = value; - this.restrictToChildOrNull = restrictToChildOrNull; - } - - final private com.gu.typesafe.config.impl.AbstractConfigValue value; - final private Path restrictToChildOrNull; - - @Override - public final int hashCode() { - int h = System.identityHashCode(value); - if (restrictToChildOrNull != null) { - return h + 41 * (41 + restrictToChildOrNull.hashCode()); - } else { - return h; - } - } - - @Override - public final boolean equals(Object other) { - if (other instanceof MemoKey) { - MemoKey o = (MemoKey) other; - if (o.value != this.value) - return false; - else if (o.restrictToChildOrNull == this.restrictToChildOrNull) - return true; - else if (o.restrictToChildOrNull == null || this.restrictToChildOrNull == null) - return false; - else - return o.restrictToChildOrNull.equals(this.restrictToChildOrNull); - } else { - return false; - } - } - - @Override - public final String toString() { - return "MemoKey(" + value + "@" + System.identityHashCode(value) + "," + restrictToChildOrNull + ")"; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/MergeableValue.java b/scripts/src/main/java/com/gu/typesafe/config/impl/MergeableValue.java deleted file mode 100644 index 007b5b1340..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/MergeableValue.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigValue; -import com.gu.typesafe.config.ConfigMergeable; - -interface MergeableValue extends ConfigMergeable { - // converts a Config to its root object and a ConfigValue to itself - ConfigValue toFallbackValue(); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/OriginType.java b/scripts/src/main/java/com/gu/typesafe/config/impl/OriginType.java deleted file mode 100644 index aae0d42eb6..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/OriginType.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gu.typesafe.config.impl; - -// caution: ordinals used in serialization -enum OriginType { - GENERIC, - FILE, - URL, - RESOURCE -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/Parseable.java b/scripts/src/main/java/com/gu/typesafe/config/impl/Parseable.java deleted file mode 100644 index 1a82c834b6..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/Parseable.java +++ /dev/null @@ -1,873 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FilterReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLConnection; -import java.util.*; - -import com.gu.typesafe.config.ConfigParseOptions; -import com.gu.typesafe.config.parser.ConfigDocument; - -/** - * Internal implementation detail, not ABI stable, do not touch. - * For use only by the {@link com.typesafe.config} package. - * The point of this class is to avoid "propagating" each - * overload on "thing which can be parsed" through multiple - * interfaces. Most interfaces can have just one overload that - * takes a Parseable. Also it's used as an abstract "resource - * handle" in the ConfigIncluder interface. - */ -public abstract class Parseable implements com.gu.typesafe.config.ConfigParseable { - private com.gu.typesafe.config.ConfigIncludeContext includeContext; - private com.gu.typesafe.config.ConfigParseOptions initialOptions; - private com.gu.typesafe.config.ConfigOrigin initialOrigin; - - /** - * Internal implementation detail, not ABI stable, do not touch. - */ - protected interface Relativizer { - com.gu.typesafe.config.ConfigParseable relativeTo(String filename); - } - - private static final ThreadLocal> parseStack = new ThreadLocal>() { - @Override - protected LinkedList initialValue() { - return new LinkedList(); - } - }; - - private static final int MAX_INCLUDE_DEPTH = 50; - - protected Parseable() { - } - - private com.gu.typesafe.config.ConfigParseOptions fixupOptions(com.gu.typesafe.config.ConfigParseOptions baseOptions) { - com.gu.typesafe.config.ConfigSyntax syntax = baseOptions.getSyntax(); - if (syntax == null) { - syntax = guessSyntax(); - } - if (syntax == null) { - syntax = com.gu.typesafe.config.ConfigSyntax.CONF; - } - com.gu.typesafe.config.ConfigParseOptions modified = baseOptions.setSyntax(syntax); - - // make sure the app-provided includer falls back to default - modified = modified.appendIncluder(ConfigImpl.defaultIncluder()); - // make sure the app-provided includer is complete - modified = modified.setIncluder(SimpleIncluder.makeFull(modified.getIncluder())); - - return modified; - } - - protected void postConstruct(com.gu.typesafe.config.ConfigParseOptions baseOptions) { - this.initialOptions = fixupOptions(baseOptions); - - this.includeContext = new SimpleIncludeContext(this); - - if (initialOptions.getOriginDescription() != null) - initialOrigin = SimpleConfigOrigin.newSimple(initialOptions.getOriginDescription()); - else - initialOrigin = createOrigin(); - } - - // the general idea is that any work should be in here, not in the - // constructor, so that exceptions are thrown from the public parse() - // function and not from the creation of the Parseable. - // Essentially this is a lazy field. The parser should close the - // reader when it's done with it. - // ALSO, IMPORTANT: if the file or URL is not found, this must throw. - // to support the "allow missing" feature. - protected abstract Reader reader() throws IOException; - - protected Reader reader(com.gu.typesafe.config.ConfigParseOptions options) throws IOException { - return reader(); - } - - protected static void trace(String message) { - if (ConfigImpl.traceLoadsEnabled()) { - ConfigImpl.trace(message); - } - } - - com.gu.typesafe.config.ConfigSyntax guessSyntax() { - return null; - } - - com.gu.typesafe.config.ConfigSyntax contentType() { - return null; - } - - com.gu.typesafe.config.ConfigParseable relativeTo(String filename) { - // fall back to classpath; we treat the "filename" as absolute - // (don't add a package name in front), - // if it starts with "/" then remove the "/", for consistency - // with ParseableResources.relativeTo - String resource = filename; - if (filename.startsWith("/")) - resource = filename.substring(1); - return newResources(resource, options().setOriginDescription(null)); - } - - com.gu.typesafe.config.ConfigIncludeContext includeContext() { - return includeContext; - } - - static AbstractConfigObject forceParsedToObject(com.gu.typesafe.config.ConfigValue value) { - if (value instanceof AbstractConfigObject) { - return (AbstractConfigObject) value; - } else { - throw new com.gu.typesafe.config.ConfigException.WrongType(value.origin(), "", "object at file root", value - .valueType().name()); - } - } - - @Override - public com.gu.typesafe.config.ConfigObject parse(com.gu.typesafe.config.ConfigParseOptions baseOptions) { - - LinkedList stack = parseStack.get(); - if (stack.size() >= MAX_INCLUDE_DEPTH) { - throw new com.gu.typesafe.config.ConfigException.Parse(initialOrigin, "include statements nested more than " - + MAX_INCLUDE_DEPTH - + " times, you probably have a cycle in your includes. Trace: " + stack); - } - - stack.addFirst(this); - try { - return forceParsedToObject(parseValue(baseOptions)); - } finally { - stack.removeFirst(); - if (stack.isEmpty()) { - parseStack.remove(); - } - } - } - - final com.gu.typesafe.config.impl.AbstractConfigValue parseValue(com.gu.typesafe.config.ConfigParseOptions baseOptions) { - // note that we are NOT using our "initialOptions", - // but using the ones from the passed-in options. The idea is that - // callers can get our original options and then parse with different - // ones if they want. - com.gu.typesafe.config.ConfigParseOptions options = fixupOptions(baseOptions); - - // passed-in options can override origin - com.gu.typesafe.config.ConfigOrigin origin; - if (options.getOriginDescription() != null) - origin = SimpleConfigOrigin.newSimple(options.getOriginDescription()); - else - origin = initialOrigin; - return parseValue(origin, options); - } - - final private com.gu.typesafe.config.impl.AbstractConfigValue parseValue(com.gu.typesafe.config.ConfigOrigin origin, - com.gu.typesafe.config.ConfigParseOptions finalOptions) { - try { - return rawParseValue(origin, finalOptions); - } catch (IOException e) { - if (finalOptions.getAllowMissing()) { - trace(e.getMessage() + ". Allowing Missing File, this can be turned off by setting" + - " ConfigParseOptions.allowMissing = false"); - return SimpleConfigObject.emptyMissing(origin); - } else { - trace("exception loading " + origin.description() + ": " + e.getClass().getName() - + ": " + e.getMessage()); - throw new com.gu.typesafe.config.ConfigException.IO(origin, - e.getClass().getName() + ": " + e.getMessage(), e); - } - } - } - - final com.gu.typesafe.config.parser.ConfigDocument parseDocument(com.gu.typesafe.config.ConfigParseOptions baseOptions) { - // note that we are NOT using our "initialOptions", - // but using the ones from the passed-in options. The idea is that - // callers can get our original options and then parse with different - // ones if they want. - com.gu.typesafe.config.ConfigParseOptions options = fixupOptions(baseOptions); - - // passed-in options can override origin - com.gu.typesafe.config.ConfigOrigin origin; - if (options.getOriginDescription() != null) - origin = SimpleConfigOrigin.newSimple(options.getOriginDescription()); - else - origin = initialOrigin; - return parseDocument(origin, options); - } - - final private com.gu.typesafe.config.parser.ConfigDocument parseDocument(com.gu.typesafe.config.ConfigOrigin origin, - com.gu.typesafe.config.ConfigParseOptions finalOptions) { - try { - return rawParseDocument(origin, finalOptions); - } catch (IOException e) { - if (finalOptions.getAllowMissing()) { - ArrayList children = new ArrayList(); - children.add(new ConfigNodeObject(new ArrayList())); - return new SimpleConfigDocument(new ConfigNodeRoot(children, origin), finalOptions); - } else { - trace("exception loading " + origin.description() + ": " + e.getClass().getName() - + ": " + e.getMessage()); - throw new com.gu.typesafe.config.ConfigException.IO(origin, - e.getClass().getName() + ": " + e.getMessage(), e); - } - } - } - - // this is parseValue without post-processing the IOException or handling - // options.getAllowMissing() - protected com.gu.typesafe.config.impl.AbstractConfigValue rawParseValue(com.gu.typesafe.config.ConfigOrigin origin, com.gu.typesafe.config.ConfigParseOptions finalOptions) - throws IOException { - Reader reader = reader(finalOptions); - - // after reader() we will have loaded the Content-Type. - com.gu.typesafe.config.ConfigSyntax contentType = contentType(); - - com.gu.typesafe.config.ConfigParseOptions optionsWithContentType; - if (contentType != null) { - if (ConfigImpl.traceLoadsEnabled() && finalOptions.getSyntax() != null) - trace("Overriding syntax " + finalOptions.getSyntax() - + " with Content-Type which specified " + contentType); - - optionsWithContentType = finalOptions.setSyntax(contentType); - } else { - optionsWithContentType = finalOptions; - } - - try { - return rawParseValue(reader, origin, optionsWithContentType); - } finally { - reader.close(); - } - } - - private com.gu.typesafe.config.impl.AbstractConfigValue rawParseValue(Reader reader, com.gu.typesafe.config.ConfigOrigin origin, - com.gu.typesafe.config.ConfigParseOptions finalOptions) throws IOException { - if (finalOptions.getSyntax() == com.gu.typesafe.config.ConfigSyntax.PROPERTIES) { - return PropertiesParser.parse(reader, origin); - } else { - Iterator tokens = Tokenizer.tokenize(origin, reader, finalOptions.getSyntax()); - ConfigNodeRoot document = ConfigDocumentParser.parse(tokens, origin, finalOptions); - return ConfigParser.parse(document, origin, finalOptions, includeContext()); - } - } - - // this is parseDocument without post-processing the IOException or handling - // options.getAllowMissing() - protected com.gu.typesafe.config.parser.ConfigDocument rawParseDocument(com.gu.typesafe.config.ConfigOrigin origin, com.gu.typesafe.config.ConfigParseOptions finalOptions) - throws IOException { - Reader reader = reader(finalOptions); - - // after reader() we will have loaded the Content-Type. - com.gu.typesafe.config.ConfigSyntax contentType = contentType(); - - com.gu.typesafe.config.ConfigParseOptions optionsWithContentType; - if (contentType != null) { - if (ConfigImpl.traceLoadsEnabled() && finalOptions.getSyntax() != null) - trace("Overriding syntax " + finalOptions.getSyntax() - + " with Content-Type which specified " + contentType); - - optionsWithContentType = finalOptions.setSyntax(contentType); - } else { - optionsWithContentType = finalOptions; - } - - try { - return rawParseDocument(reader, origin, optionsWithContentType); - } finally { - reader.close(); - } - } - - private com.gu.typesafe.config.parser.ConfigDocument rawParseDocument(Reader reader, com.gu.typesafe.config.ConfigOrigin origin, - com.gu.typesafe.config.ConfigParseOptions finalOptions) throws IOException { - Iterator tokens = Tokenizer.tokenize(origin, reader, finalOptions.getSyntax()); - return new SimpleConfigDocument(ConfigDocumentParser.parse(tokens, origin, finalOptions), finalOptions); - } - - public com.gu.typesafe.config.ConfigObject parse() { - return forceParsedToObject(parseValue(options())); - } - - public ConfigDocument parseConfigDocument() { - return parseDocument(options()); - } - - com.gu.typesafe.config.impl.AbstractConfigValue parseValue() { - return parseValue(options()); - } - - @Override - public final com.gu.typesafe.config.ConfigOrigin origin() { - return initialOrigin; - } - - protected abstract com.gu.typesafe.config.ConfigOrigin createOrigin(); - - @Override - public com.gu.typesafe.config.ConfigParseOptions options() { - return initialOptions; - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } - - private static Reader readerFromStream(InputStream input) { - return readerFromStream(input, "UTF-8"); - } - - private static Reader readerFromStream(InputStream input, String encoding) { - try { - // well, this is messed up. If we aren't going to close - // the passed-in InputStream then we have no way to - // close these readers. So maybe we should not have an - // InputStream version, only a Reader version. - Reader reader = new InputStreamReader(input, encoding); - return new BufferedReader(reader); - } catch (UnsupportedEncodingException e) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Java runtime does not support UTF-8", e); - } - } - - private static Reader doNotClose(Reader input) { - return new FilterReader(input) { - @Override - public void close() { - // NOTHING. - } - }; - } - - static URL relativeTo(URL url, String filename) { - // I'm guessing this completely fails on Windows, help wanted - if (new File(filename).isAbsolute()) - return null; - - try { - URI siblingURI = url.toURI(); - URI relative = new URI(filename); - - // this seems wrong, but it's documented that the last - // element of the path in siblingURI gets stripped out, - // so to get something in the same directory as - // siblingURI we just call resolve(). - URL resolved = siblingURI.resolve(relative).toURL(); - - return resolved; - } catch (MalformedURLException e) { - return null; - } catch (URISyntaxException e) { - return null; - } catch (IllegalArgumentException e) { - return null; - } - } - - static File relativeTo(File file, String filename) { - File child = new File(filename); - - if (child.isAbsolute()) - return null; - - File parent = file.getParentFile(); - - if (parent == null) - return null; - else - return new File(parent, filename); - } - - // this is a parseable that doesn't exist and just throws when you try to - // parse it - private final static class ParseableNotFound extends Parseable { - final private String what; - final private String message; - - ParseableNotFound(String what, String message, com.gu.typesafe.config.ConfigParseOptions options) { - this.what = what; - this.message = message; - postConstruct(options); - } - - @Override - protected Reader reader() throws IOException { - throw new FileNotFoundException(message); - } - - @Override - protected com.gu.typesafe.config.ConfigOrigin createOrigin() { - return SimpleConfigOrigin.newSimple(what); - } - } - - public static Parseable newNotFound(String whatNotFound, String message, - com.gu.typesafe.config.ConfigParseOptions options) { - return new ParseableNotFound(whatNotFound, message, options); - } - - private final static class ParseableReader extends Parseable { - final private Reader reader; - - ParseableReader(Reader reader, com.gu.typesafe.config.ConfigParseOptions options) { - this.reader = reader; - postConstruct(options); - } - - @Override - protected Reader reader() { - if (ConfigImpl.traceLoadsEnabled()) - trace("Loading config from reader " + reader); - return reader; - } - - @Override - protected com.gu.typesafe.config.ConfigOrigin createOrigin() { - return SimpleConfigOrigin.newSimple("Reader"); - } - } - - // note that we will never close this reader; you have to do it when parsing - // is complete. - public static Parseable newReader(Reader reader, com.gu.typesafe.config.ConfigParseOptions options) { - - return new ParseableReader(doNotClose(reader), options); - } - - private final static class ParseableString extends Parseable { - final private String input; - - ParseableString(String input, com.gu.typesafe.config.ConfigParseOptions options) { - this.input = input; - postConstruct(options); - } - - @Override - protected Reader reader() { - if (ConfigImpl.traceLoadsEnabled()) - trace("Loading config from a String " + input); - return new StringReader(input); - } - - @Override - protected com.gu.typesafe.config.ConfigOrigin createOrigin() { - return SimpleConfigOrigin.newSimple("String"); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "(" + input + ")"; - } - } - - public static Parseable newString(String input, com.gu.typesafe.config.ConfigParseOptions options) { - return new ParseableString(input, options); - } - - private static final String jsonContentType = "application/json"; - private static final String propertiesContentType = "text/x-java-properties"; - private static final String hoconContentType = "application/hocon"; - - private static class ParseableURL extends Parseable { - final protected URL input; - private String contentType = null; - - protected ParseableURL(URL input) { - this.input = input; - // does not postConstruct (subclass does it) - } - - ParseableURL(URL input, com.gu.typesafe.config.ConfigParseOptions options) { - this(input); - postConstruct(options); - } - - @Override - protected Reader reader() throws IOException { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("reader() without options should not be called on ParseableURL"); - } - - private static String acceptContentType(com.gu.typesafe.config.ConfigParseOptions options) { - if (options.getSyntax() == null) - return null; - - switch (options.getSyntax()) { - case JSON: - return jsonContentType; - case CONF: - return hoconContentType; - case PROPERTIES: - return propertiesContentType; - } - - // not sure this is reachable but javac thinks it is - return null; - } - - @Override - protected Reader reader(com.gu.typesafe.config.ConfigParseOptions options) throws IOException { - try { - if (ConfigImpl.traceLoadsEnabled()) - trace("Loading config from a URL: " + input.toExternalForm()); - URLConnection connection = input.openConnection(); - - // allow server to serve multiple types from one URL - String acceptContent = acceptContentType(options); - if (acceptContent != null) { - connection.setRequestProperty("Accept", acceptContent); - } - - connection.connect(); - - // save content type for later - contentType = connection.getContentType(); - if (contentType != null) { - if (ConfigImpl.traceLoadsEnabled()) - trace("URL sets Content-Type: '" + contentType + "'"); - contentType = contentType.trim(); - int semi = contentType.indexOf(';'); - if (semi >= 0) - contentType = contentType.substring(0, semi); - } - - InputStream stream = connection.getInputStream(); - - return readerFromStream(stream); - } catch (FileNotFoundException fnf) { - // If the resource is not found (HTTP response - // code 404 or something alike), then it's fine to - // treat it according to the allowMissing setting - // and "include" spec. But if we have something - // like HTTP 503 it seems to be better to fail - // early, because this may be a sign of broken - // environment. Java throws FileNotFoundException - // if it sees 404 or 410. - throw fnf; - } catch (IOException e) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Cannot load config from URL: " + input.toExternalForm(), e); - } - } - - @Override - com.gu.typesafe.config.ConfigSyntax guessSyntax() { - return ConfigImplUtil.syntaxFromExtension(input.getPath()); - } - - @Override - com.gu.typesafe.config.ConfigSyntax contentType() { - if (contentType != null) { - if (contentType.equals(jsonContentType)) - return com.gu.typesafe.config.ConfigSyntax.JSON; - else if (contentType.equals(propertiesContentType)) - return com.gu.typesafe.config.ConfigSyntax.PROPERTIES; - else if (contentType.equals(hoconContentType)) - return com.gu.typesafe.config.ConfigSyntax.CONF; - else { - if (ConfigImpl.traceLoadsEnabled()) - trace("'" + contentType + "' isn't a known content type"); - return null; - } - } else { - return null; - } - } - - @Override - com.gu.typesafe.config.ConfigParseable relativeTo(String filename) { - URL url = relativeTo(input, filename); - if (url == null) - return null; - return newURL(url, options().setOriginDescription(null)); - } - - @Override - protected com.gu.typesafe.config.ConfigOrigin createOrigin() { - return SimpleConfigOrigin.newURL(input); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "(" + input.toExternalForm() + ")"; - } - } - - public static Parseable newURL(URL input, com.gu.typesafe.config.ConfigParseOptions options) { - // we want file: URLs and files to always behave the same, so switch - // to a file if it's a file: URL - if (input.getProtocol().equals("file")) { - return newFile(ConfigImplUtil.urlToFile(input), options); - } else { - return new ParseableURL(input, options); - } - } - - private final static class ParseableFile extends Parseable { - final private File input; - - ParseableFile(File input, com.gu.typesafe.config.ConfigParseOptions options) { - this.input = input; - postConstruct(options); - } - - @Override - protected Reader reader() throws IOException { - if (ConfigImpl.traceLoadsEnabled()) - trace("Loading config from a file: " + input); - InputStream stream = new FileInputStream(input); - return readerFromStream(stream); - } - - @Override - com.gu.typesafe.config.ConfigSyntax guessSyntax() { - return ConfigImplUtil.syntaxFromExtension(input.getName()); - } - - @Override - com.gu.typesafe.config.ConfigParseable relativeTo(String filename) { - File sibling; - if ((new File(filename)).isAbsolute()) { - sibling = new File(filename); - } else { - // this may return null - sibling = relativeTo(input, filename); - } - if (sibling == null) - return null; - if (sibling.exists()) { - trace(sibling + " exists, so loading it as a file"); - return newFile(sibling, options().setOriginDescription(null)); - } else { - trace(sibling + " does not exist, so trying it as a classpath resource"); - return super.relativeTo(filename); - } - } - - @Override - protected com.gu.typesafe.config.ConfigOrigin createOrigin() { - return SimpleConfigOrigin.newFile(input.getPath()); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "(" + input.getPath() + ")"; - } - } - - public static Parseable newFile(File input, com.gu.typesafe.config.ConfigParseOptions options) { - return new ParseableFile(input, options); - } - - - private final static class ParseableResourceURL extends ParseableURL { - - private final Relativizer relativizer; - private final String resource; - - ParseableResourceURL(URL input, com.gu.typesafe.config.ConfigParseOptions options, String resource, Relativizer relativizer) { - super(input); - this.relativizer = relativizer; - this.resource = resource; - postConstruct(options); - } - - @Override - protected com.gu.typesafe.config.ConfigOrigin createOrigin() { - return SimpleConfigOrigin.newResource(resource, input); - } - - @Override - com.gu.typesafe.config.ConfigParseable relativeTo(String filename) { - return relativizer.relativeTo(filename); - } - } - - private static Parseable newResourceURL(URL input, com.gu.typesafe.config.ConfigParseOptions options, String resource, Relativizer relativizer) { - return new ParseableResourceURL(input, options, resource, relativizer); - } - - private final static class ParseableResources extends Parseable implements Relativizer { - final private String resource; - - ParseableResources(String resource, com.gu.typesafe.config.ConfigParseOptions options) { - this.resource = resource; - postConstruct(options); - } - - @Override - protected Reader reader() throws IOException { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("reader() should not be called on resources"); - } - - @Override - protected AbstractConfigObject rawParseValue(com.gu.typesafe.config.ConfigOrigin origin, - com.gu.typesafe.config.ConfigParseOptions finalOptions) throws IOException { - ClassLoader loader = finalOptions.getClassLoader(); - if (loader == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "null class loader; pass in a class loader or use Thread.currentThread().setContextClassLoader()"); - Enumeration e = loader.getResources(resource); - if (!e.hasMoreElements()) { - if (ConfigImpl.traceLoadsEnabled()) - trace("Loading config from class loader " + loader - + " but there were no resources called " + resource); - throw new IOException("resource not found on classpath: " + resource); - } - AbstractConfigObject merged = SimpleConfigObject.empty(origin); - while (e.hasMoreElements()) { - URL url = e.nextElement(); - - if (ConfigImpl.traceLoadsEnabled()) - trace("Loading config from resource '" + resource + "' URL " + url.toExternalForm() + " from class loader " - + loader); - - Parseable element = newResourceURL(url, finalOptions, resource, this); - - com.gu.typesafe.config.impl.AbstractConfigValue v = element.parseValue(); - - merged = merged.withFallback(v); - } - - return merged; - } - - @Override - com.gu.typesafe.config.ConfigSyntax guessSyntax() { - return ConfigImplUtil.syntaxFromExtension(resource); - } - - static String parent(String resource) { - // the "resource" is not supposed to begin with a "/" - // because it's supposed to be the raw resource - // (ClassLoader#getResource), not the - // resource "syntax" (Class#getResource) - int i = resource.lastIndexOf('/'); - if (i < 0) { - return null; - } else { - return resource.substring(0, i); - } - } - - @Override - public com.gu.typesafe.config.ConfigParseable relativeTo(String sibling) { - if (sibling.startsWith("/")) { - // if it starts with "/" then don't make it relative to - // the including resource - return newResources(sibling.substring(1), options().setOriginDescription(null)); - } else { - // here we want to build a new resource name and let - // the class loader have it, rather than getting the - // url with getResource() and relativizing to that url. - // This is needed in case the class loader is going to - // search a classpath. - String parent = parent(resource); - if (parent == null) - return newResources(sibling, options().setOriginDescription(null)); - else - return newResources(parent + "/" + sibling, options() - .setOriginDescription(null)); - } - } - - @Override - protected com.gu.typesafe.config.ConfigOrigin createOrigin() { - return SimpleConfigOrigin.newResource(resource); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "(" + resource + ")"; - } - } - - public static Parseable newResources(Class klass, String resource, com.gu.typesafe.config.ConfigParseOptions options) { - return newResources(convertResourceName(klass, resource), - options.setClassLoader(klass.getClassLoader())); - } - - // this function is supposed to emulate the difference - // between Class.getResource and ClassLoader.getResource - // (unfortunately there doesn't seem to be public API for it). - // We're using it because the Class API is more limited, - // for example it lacks getResources(). So we want to be able to - // use ClassLoader directly. - private static String convertResourceName(Class klass, String resource) { - if (resource.startsWith("/")) { - // "absolute" resource, chop the slash - return resource.substring(1); - } else { - String className = klass.getName(); - int i = className.lastIndexOf('.'); - if (i < 0) { - // no package - return resource; - } else { - // need to be relative to the package - String packageName = className.substring(0, i); - String packagePath = packageName.replace('.', '/'); - return packagePath + "/" + resource; - } - } - } - - public static Parseable newResources(String resource, com.gu.typesafe.config.ConfigParseOptions options) { - if (options.getClassLoader() == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "null class loader; pass in a class loader or use Thread.currentThread().setContextClassLoader()"); - return new ParseableResources(resource, options); - } - - private final static class ParseableProperties extends Parseable { - final private Properties props; - - ParseableProperties(Properties props, com.gu.typesafe.config.ConfigParseOptions options) { - this.props = props; - postConstruct(options); - } - - @Override - protected Reader reader() throws IOException { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("reader() should not be called on props"); - } - - @Override - protected AbstractConfigObject rawParseValue(com.gu.typesafe.config.ConfigOrigin origin, - com.gu.typesafe.config.ConfigParseOptions finalOptions) { - if (ConfigImpl.traceLoadsEnabled()) - trace("Loading config from properties " + props); - return PropertiesParser.fromProperties(origin, props); - } - - @Override - com.gu.typesafe.config.ConfigSyntax guessSyntax() { - return com.gu.typesafe.config.ConfigSyntax.PROPERTIES; - } - - @Override - protected com.gu.typesafe.config.ConfigOrigin createOrigin() { - return SimpleConfigOrigin.newSimple("properties"); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "(" + props.size() + " props)"; - } - } - - public static Parseable newProperties(Properties properties, ConfigParseOptions options) { - return new ParseableProperties(properties, options); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/Path.java b/scripts/src/main/java/com/gu/typesafe/config/impl/Path.java deleted file mode 100644 index e0259a5e10..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/Path.java +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.*; - -import com.gu.typesafe.config.ConfigException; - -final class Path { - - final private String first; - final private Path remainder; - - Path(String first, Path remainder) { - this.first = first; - this.remainder = remainder; - } - - Path(String... elements) { - if (elements.length == 0) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("empty path"); - this.first = elements[0]; - if (elements.length > 1) { - com.gu.typesafe.config.impl.PathBuilder pb = new com.gu.typesafe.config.impl.PathBuilder(); - for (int i = 1; i < elements.length; ++i) { - pb.appendKey(elements[i]); - } - this.remainder = pb.result(); - } else { - this.remainder = null; - } - } - - // append all the paths in the list together into one path - Path(List pathsToConcat) { - this(pathsToConcat.iterator()); - } - - // append all the paths in the iterator together into one path - Path(Iterator i) { - if (!i.hasNext()) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("empty path"); - - Path firstPath = i.next(); - this.first = firstPath.first; - - com.gu.typesafe.config.impl.PathBuilder pb = new com.gu.typesafe.config.impl.PathBuilder(); - if (firstPath.remainder != null) { - pb.appendPath(firstPath.remainder); - } - while (i.hasNext()) { - pb.appendPath(i.next()); - } - this.remainder = pb.result(); - } - - String first() { - return first; - } - - /** - * - * @return path minus the first element or null if no more elements - */ - Path remainder() { - return remainder; - } - - /** - * - * @return path minus the last element or null if we have just one element - */ - Path parent() { - if (remainder == null) - return null; - - com.gu.typesafe.config.impl.PathBuilder pb = new com.gu.typesafe.config.impl.PathBuilder(); - Path p = this; - while (p.remainder != null) { - pb.appendKey(p.first); - p = p.remainder; - } - return pb.result(); - } - - /** - * - * @return last element in the path - */ - String last() { - Path p = this; - while (p.remainder != null) { - p = p.remainder; - } - return p.first; - } - - Path prepend(Path toPrepend) { - com.gu.typesafe.config.impl.PathBuilder pb = new com.gu.typesafe.config.impl.PathBuilder(); - pb.appendPath(toPrepend); - pb.appendPath(this); - return pb.result(); - } - - int length() { - int count = 1; - Path p = remainder; - while (p != null) { - count += 1; - p = p.remainder; - } - return count; - } - - Path subPath(int removeFromFront) { - int count = removeFromFront; - Path p = this; - while (p != null && count > 0) { - count -= 1; - p = p.remainder; - } - return p; - } - - Path subPath(int firstIndex, int lastIndex) { - if (lastIndex < firstIndex) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("bad call to subPath"); - - Path from = subPath(firstIndex); - com.gu.typesafe.config.impl.PathBuilder pb = new com.gu.typesafe.config.impl.PathBuilder(); - int count = lastIndex - firstIndex; - while (count > 0) { - count -= 1; - pb.appendKey(from.first()); - from = from.remainder(); - if (from == null) - throw new ConfigException.BugOrBroken("subPath lastIndex out of range " + lastIndex); - } - return pb.result(); - } - - boolean startsWith(Path other) { - Path myRemainder = this; - Path otherRemainder = other; - if (otherRemainder.length() <= myRemainder.length()) { - while(otherRemainder != null) { - if (!otherRemainder.first().equals(myRemainder.first())) - return false; - myRemainder = myRemainder.remainder(); - otherRemainder = otherRemainder.remainder(); - } - return true; - } - return false; - } - - @Override - public boolean equals(Object other) { - if (other instanceof Path) { - Path that = (Path) other; - return this.first.equals(that.first) - && com.gu.typesafe.config.impl.ConfigImplUtil.equalsHandlingNull(this.remainder, - that.remainder); - } else { - return false; - } - } - - @Override - public int hashCode() { - return 41 * (41 + first.hashCode()) - + (remainder == null ? 0 : remainder.hashCode()); - } - - // this doesn't have a very precise meaning, just to reduce - // noise from quotes in the rendered path for average cases - static boolean hasFunkyChars(String s) { - int length = s.length(); - - if (length == 0) - return false; - - for (int i = 0; i < length; ++i) { - char c = s.charAt(i); - - if (Character.isLetterOrDigit(c) || c == '-' || c == '_') - continue; - else - return true; - } - return false; - } - - private void appendToStringBuilder(StringBuilder sb) { - if (hasFunkyChars(first) || first.isEmpty()) - sb.append(ConfigImplUtil.renderJsonString(first)); - else - sb.append(first); - if (remainder != null) { - sb.append("."); - remainder.appendToStringBuilder(sb); - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Path("); - appendToStringBuilder(sb); - sb.append(")"); - return sb.toString(); - } - - /** - * toString() is a debugging-oriented version while this is an - * error-message-oriented human-readable one. - */ - String render() { - StringBuilder sb = new StringBuilder(); - appendToStringBuilder(sb); - return sb.toString(); - } - - static Path newKey(String key) { - return new Path(key, null); - } - - static Path newPath(String path) { - return com.gu.typesafe.config.impl.PathParser.parsePath(path); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/PathBuilder.java b/scripts/src/main/java/com/gu/typesafe/config/impl/PathBuilder.java deleted file mode 100644 index 1989d695ac..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/PathBuilder.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigException; - -import java.util.Stack; - -final class PathBuilder { - // the keys are kept "backward" (top of stack is end of path) - final private Stack keys; - private Path result; - - PathBuilder() { - keys = new Stack(); - } - - private void checkCanAppend() { - if (result != null) - throw new ConfigException.BugOrBroken( - "Adding to PathBuilder after getting result"); - } - - void appendKey(String key) { - checkCanAppend(); - - keys.push(key); - } - - void appendPath(Path path) { - checkCanAppend(); - - String first = path.first(); - Path remainder = path.remainder(); - while (true) { - keys.push(first); - if (remainder != null) { - first = remainder.first(); - remainder = remainder.remainder(); - } else { - break; - } - } - } - - Path result() { - // note: if keys is empty, we want to return null, which is a valid - // empty path - if (result == null) { - Path remainder = null; - while (!keys.isEmpty()) { - String key = keys.pop(); - remainder = new Path(key, remainder); - } - result = remainder; - } - return result; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/PathParser.java b/scripts/src/main/java/com/gu/typesafe/config/impl/PathParser.java deleted file mode 100644 index e78e59debd..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/PathParser.java +++ /dev/null @@ -1,281 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigSyntax; -import com.gu.typesafe.config.ConfigValueType; - -import java.io.StringReader; -import java.util.*; - -final class PathParser { - static class Element { - StringBuilder sb; - // an element can be empty if it has a quoted empty string "" in it - boolean canBeEmpty; - - Element(String initial, boolean canBeEmpty) { - this.canBeEmpty = canBeEmpty; - this.sb = new StringBuilder(initial); - } - - @Override - public String toString() { - return "Element(" + sb.toString() + "," + canBeEmpty + ")"; - } - } - - static com.gu.typesafe.config.ConfigOrigin apiOrigin = SimpleConfigOrigin.newSimple("path parameter"); - - static ConfigNodePath parsePathNode(String path) { - return parsePathNode(path, com.gu.typesafe.config.ConfigSyntax.CONF); - } - - static ConfigNodePath parsePathNode(String path, com.gu.typesafe.config.ConfigSyntax flavor) { - StringReader reader = new StringReader(path); - - try { - Iterator tokens = Tokenizer.tokenize(apiOrigin, reader, - flavor); - tokens.next(); // drop START - return parsePathNodeExpression(tokens, apiOrigin, path, flavor); - } finally { - reader.close(); - } - } - - static Path parsePath(String path) { - Path speculated = speculativeFastParsePath(path); - if (speculated != null) - return speculated; - - StringReader reader = new StringReader(path); - - try { - Iterator tokens = Tokenizer.tokenize(apiOrigin, reader, - com.gu.typesafe.config.ConfigSyntax.CONF); - tokens.next(); // drop START - return parsePathExpression(tokens, apiOrigin, path); - } finally { - reader.close(); - } - } - - protected static Path parsePathExpression(Iterator expression, - com.gu.typesafe.config.ConfigOrigin origin) { - return parsePathExpression(expression, origin, null, null, com.gu.typesafe.config.ConfigSyntax.CONF); - } - - protected static Path parsePathExpression(Iterator expression, - com.gu.typesafe.config.ConfigOrigin origin, String originalText) { - return parsePathExpression(expression, origin, originalText, null, com.gu.typesafe.config.ConfigSyntax.CONF); - } - - protected static ConfigNodePath parsePathNodeExpression(Iterator expression, - com.gu.typesafe.config.ConfigOrigin origin) { - return parsePathNodeExpression(expression, origin, null, com.gu.typesafe.config.ConfigSyntax.CONF); - } - - protected static ConfigNodePath parsePathNodeExpression(Iterator expression, - com.gu.typesafe.config.ConfigOrigin origin, String originalText, com.gu.typesafe.config.ConfigSyntax flavor) { - ArrayList pathTokens = new ArrayList(); - Path path = parsePathExpression(expression, origin, originalText, pathTokens, flavor); - return new ConfigNodePath(path, pathTokens); - } - - // originalText may be null if not available - protected static Path parsePathExpression(Iterator expression, - ConfigOrigin origin, String originalText, - ArrayList pathTokens, - com.gu.typesafe.config.ConfigSyntax flavor) { - // each builder in "buf" is an element in the path. - List buf = new ArrayList(); - buf.add(new Element("", false)); - - if (!expression.hasNext()) { - throw new com.gu.typesafe.config.ConfigException.BadPath(origin, originalText, - "Expecting a field name or path here, but got nothing"); - } - - while (expression.hasNext()) { - Token t = expression.next(); - - if (pathTokens != null) - pathTokens.add(t); - - // Ignore all IgnoredWhitespace tokens - if (Tokens.isIgnoredWhitespace(t)) - continue; - - if (Tokens.isValueWithType(t, ConfigValueType.STRING)) { - com.gu.typesafe.config.impl.AbstractConfigValue v = Tokens.getValue(t); - // this is a quoted string; so any periods - // in here don't count as path separators - String s = v.transformToString(); - - addPathText(buf, true, s); - } else if (t == Tokens.END) { - // ignore this; when parsing a file, it should not happen - // since we're parsing a token list rather than the main - // token iterator, and when parsing a path expression from the - // API, it's expected to have an END. - } else { - // any periods outside of a quoted string count as - // separators - String text; - if (Tokens.isValue(t)) { - // appending a number here may add - // a period, but we _do_ count those as path - // separators, because we basically want - // "foo 3.0bar" to parse as a string even - // though there's a number in it. The fact that - // we tokenize non-string values is largely an - // implementation detail. - com.gu.typesafe.config.impl.AbstractConfigValue v = Tokens.getValue(t); - - // We need to split the tokens on a . so that we can get sub-paths but still preserve - // the original path text when doing an insertion - if (pathTokens != null) { - pathTokens.remove(pathTokens.size() - 1); - pathTokens.addAll(splitTokenOnPeriod(t, flavor)); - } - text = v.transformToString(); - } else if (Tokens.isUnquotedText(t)) { - // We need to split the tokens on a . so that we can get sub-paths but still preserve - // the original path text when doing an insertion on ConfigNodeObjects - if (pathTokens != null) { - pathTokens.remove(pathTokens.size() - 1); - pathTokens.addAll(splitTokenOnPeriod(t, flavor)); - } - text = Tokens.getUnquotedText(t); - } else { - throw new com.gu.typesafe.config.ConfigException.BadPath( - origin, - originalText, - "Token not allowed in path expression: " - + t - + " (you can double-quote this token if you really want it here)"); - } - - addPathText(buf, false, text); - } - } - - com.gu.typesafe.config.impl.PathBuilder pb = new com.gu.typesafe.config.impl.PathBuilder(); - for (Element e : buf) { - if (e.sb.length() == 0 && !e.canBeEmpty) { - throw new ConfigException.BadPath( - origin, - originalText, - "path has a leading, trailing, or two adjacent period '.' (use quoted \"\" empty string if you want an empty element)"); - } else { - pb.appendKey(e.sb.toString()); - } - } - - return pb.result(); - } - - private static Collection splitTokenOnPeriod(Token t, com.gu.typesafe.config.ConfigSyntax flavor) { - String tokenText = t.tokenText(); - if (tokenText.equals(".")) { - return Collections.singletonList(t); - } - String[] splitToken = tokenText.split("\\."); - ArrayList splitTokens = new ArrayList(); - for (String s : splitToken) { - if (flavor == ConfigSyntax.CONF) - splitTokens.add(Tokens.newUnquotedText(t.origin(), s)); - else - splitTokens.add(Tokens.newString(t.origin(), s, "\"" + s + "\"")); - splitTokens.add(Tokens.newUnquotedText(t.origin(), ".")); - } - if (tokenText.charAt(tokenText.length() - 1) != '.') - splitTokens.remove(splitTokens.size() - 1); - return splitTokens; - } - - private static void addPathText(List buf, boolean wasQuoted, - String newText) { - int i = wasQuoted ? -1 : newText.indexOf('.'); - Element current = buf.get(buf.size() - 1); - if (i < 0) { - // add to current path element - current.sb.append(newText); - // any empty quoted string means this element can - // now be empty. - if (wasQuoted && current.sb.length() == 0) - current.canBeEmpty = true; - } else { - // "buf" plus up to the period is an element - current.sb.append(newText.substring(0, i)); - // then start a new element - buf.add(new Element("", false)); - // recurse to consume remainder of newText - addPathText(buf, false, newText.substring(i + 1)); - } - } - - // the idea is to see if the string has any chars or features - // that might require the full parser to deal with. - private static boolean looksUnsafeForFastParser(String s) { - boolean lastWasDot = true; // start of path is also a "dot" - int len = s.length(); - if (s.isEmpty()) - return true; - if (s.charAt(0) == '.') - return true; - if (s.charAt(len - 1) == '.') - return true; - - for (int i = 0; i < len; ++i) { - char c = s.charAt(i); - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') { - lastWasDot = false; - continue; - } else if (c == '.') { - if (lastWasDot) - return true; // ".." means we need to throw an error - lastWasDot = true; - } else if (c == '-') { - if (lastWasDot) - return true; - continue; - } else { - return true; - } - } - - if (lastWasDot) - return true; - - return false; - } - - private static Path fastPathBuild(Path tail, String s, int end) { - // lastIndexOf takes last index it should look at, end - 1 not end - int splitAt = s.lastIndexOf('.', end - 1); - ArrayList tokens = new ArrayList(); - tokens.add(Tokens.newUnquotedText(null, s)); - // this works even if splitAt is -1; then we start the substring at 0 - Path withOneMoreElement = new Path(s.substring(splitAt + 1, end), tail); - if (splitAt < 0) { - return withOneMoreElement; - } else { - return fastPathBuild(withOneMoreElement, s, splitAt); - } - } - - // do something much faster than the full parser if - // we just have something like "foo" or "foo.bar" - private static Path speculativeFastParsePath(String path) { - String s = ConfigImplUtil.unicodeTrim(path); - if (looksUnsafeForFastParser(s)) - return null; - - return fastPathBuild(null, s, s.length()); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/PropertiesParser.java b/scripts/src/main/java/com/gu/typesafe/config/impl/PropertiesParser.java deleted file mode 100644 index c4cd21606f..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/PropertiesParser.java +++ /dev/null @@ -1,210 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.IOException; -import java.io.Reader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; - -final class PropertiesParser { - static AbstractConfigObject parse(Reader reader, - com.gu.typesafe.config.ConfigOrigin origin) throws IOException { - Properties props = new Properties(); - props.load(reader); - return fromProperties(origin, props); - } - - static String lastElement(String path) { - int i = path.lastIndexOf('.'); - if (i < 0) - return path; - else - return path.substring(i + 1); - } - - static String exceptLastElement(String path) { - int i = path.lastIndexOf('.'); - if (i < 0) - return null; - else - return path.substring(0, i); - } - - static Path pathFromPropertyKey(String key) { - String last = lastElement(key); - String exceptLast = exceptLastElement(key); - Path path = new Path(last, null); - while (exceptLast != null) { - last = lastElement(exceptLast); - exceptLast = exceptLastElement(exceptLast); - path = new Path(last, path); - } - return path; - } - - static AbstractConfigObject fromProperties(com.gu.typesafe.config.ConfigOrigin origin, - Properties props) { - return fromEntrySet(origin, props.entrySet()); - } - - private static AbstractConfigObject fromEntrySet(com.gu.typesafe.config.ConfigOrigin origin, Set> entries) { - final Map pathMap = getPathMap(entries); - return fromPathMap(origin, pathMap, true /* from properties */); - } - - private static Map getPathMap(Set> entries) { - Map pathMap = new HashMap(); - for (Map.Entry entry : entries) { - Object key = entry.getKey(); - if (key instanceof String) { - Path path = pathFromPropertyKey((String) key); - pathMap.put(path, entry.getValue()); - } - } - return pathMap; - } - - static AbstractConfigObject fromStringMap(com.gu.typesafe.config.ConfigOrigin origin, Map stringMap) { - return fromEntrySet(origin, stringMap.entrySet()); - } - - static AbstractConfigObject fromPathMap(com.gu.typesafe.config.ConfigOrigin origin, - Map pathExpressionMap) { - Map pathMap = new HashMap(); - for (Map.Entry entry : pathExpressionMap.entrySet()) { - Object keyObj = entry.getKey(); - if (!(keyObj instanceof String)) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "Map has a non-string as a key, expecting a path expression as a String"); - } - Path path = Path.newPath((String) keyObj); - pathMap.put(path, entry.getValue()); - } - return fromPathMap(origin, pathMap, false /* from properties */); - } - - private static AbstractConfigObject fromPathMap(ConfigOrigin origin, - Map pathMap, boolean convertedFromProperties) { - /* - * First, build a list of paths that will have values, either string or - * object values. - */ - Set scopePaths = new HashSet(); - Set valuePaths = new HashSet(); - for (Path path : pathMap.keySet()) { - // add value's path - valuePaths.add(path); - - // all parent paths are objects - Path next = path.parent(); - while (next != null) { - scopePaths.add(next); - next = next.parent(); - } - } - - if (convertedFromProperties) { - /* - * If any string values are also objects containing other values, - * drop those string values - objects "win". - */ - valuePaths.removeAll(scopePaths); - } else { - /* If we didn't start out as properties, then this is an error. */ - for (Path path : valuePaths) { - if (scopePaths.contains(path)) { - throw new ConfigException.BugOrBroken( - "In the map, path '" - + path.render() - + "' occurs as both the parent object of a value and as a value. " - + "Because Map has no defined ordering, this is a broken situation."); - } - } - } - - /* - * Create maps for the object-valued values. - */ - Map root = new HashMap(); - Map> scopes = new HashMap>(); - - for (Path path : scopePaths) { - Map scope = new HashMap(); - scopes.put(path, scope); - } - - /* Store string values in the associated scope maps */ - for (Path path : valuePaths) { - Path parentPath = path.parent(); - Map parent = parentPath != null ? scopes - .get(parentPath) : root; - - String last = path.last(); - Object rawValue = pathMap.get(path); - com.gu.typesafe.config.impl.AbstractConfigValue value; - if (convertedFromProperties) { - if (rawValue instanceof String) { - value = new com.gu.typesafe.config.impl.ConfigString.Quoted(origin, (String) rawValue); - } else { - // silently ignore non-string values in Properties - value = null; - } - } else { - value = ConfigImpl.fromAnyRef(pathMap.get(path), origin, - FromMapMode.KEYS_ARE_PATHS); - } - if (value != null) - parent.put(last, value); - } - - /* - * Make a list of scope paths from longest to shortest, so children go - * before parents. - */ - List sortedScopePaths = new ArrayList(); - sortedScopePaths.addAll(scopePaths); - // sort descending by length - Collections.sort(sortedScopePaths, new Comparator() { - @Override - public int compare(Path a, Path b) { - // Path.length() is O(n) so in theory this sucks - // but in practice we can make Path precompute length - // if it ever matters. - return b.length() - a.length(); - } - }); - - /* - * Create ConfigObject for each scope map, working from children to - * parents to avoid modifying any already-created ConfigObject. This is - * where we need the sorted list. - */ - for (Path scopePath : sortedScopePaths) { - Map scope = scopes.get(scopePath); - - Path parentPath = scopePath.parent(); - Map parent = parentPath != null ? scopes - .get(parentPath) : root; - - AbstractConfigObject o = new com.gu.typesafe.config.impl.SimpleConfigObject(origin, scope, - ResolveStatus.RESOLVED, false /* ignoresFallbacks */); - parent.put(scopePath.last(), o); - } - - // return root config object - return new com.gu.typesafe.config.impl.SimpleConfigObject(origin, root, ResolveStatus.RESOLVED, - false /* ignoresFallbacks */); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ReplaceableMergeStack.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ReplaceableMergeStack.java deleted file mode 100644 index 0d532982ff..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ReplaceableMergeStack.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gu.typesafe.config.impl; - -/** - * Implemented by a merge stack (ConfigDelayedMerge, ConfigDelayedMergeObject) - * that replaces itself during substitution resolution in order to implement - * "look backwards only" semantics. - */ -interface ReplaceableMergeStack extends Container { - /** - * Make a replacement for this object skipping the given number of elements - * which are lower in merge priority. - */ - com.gu.typesafe.config.impl.AbstractConfigValue makeReplacement(com.gu.typesafe.config.impl.ResolveContext context, int skipping); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveContext.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveContext.java deleted file mode 100644 index 764d64afcc..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveContext.java +++ /dev/null @@ -1,242 +0,0 @@ -package com.gu.typesafe.config.impl; - -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.ArrayList; -import java.util.Set; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigResolveOptions; -import com.gu.typesafe.config.impl.AbstractConfigValue.NotPossibleToResolve; - -final class ResolveContext { - final private ResolveMemos memos; - - final private ConfigResolveOptions options; - // the current path restriction, used to ensure lazy - // resolution and avoid gratuitous cycles. without this, - // any sibling of an object we're traversing could - // cause a cycle "by side effect" - // CAN BE NULL for a full resolve. - final private Path restrictToChild; - - // This is used for tracing and debugging and nice error messages; - // contains every node as we call resolve on it. - final private List resolveStack; - - final private Set cycleMarkers; - - ResolveContext(ResolveMemos memos, ConfigResolveOptions options, Path restrictToChild, - List resolveStack, Set cycleMarkers) { - this.memos = memos; - this.options = options; - this.restrictToChild = restrictToChild; - // we don't defensively copy/wrap these because Collections.unmodifiableList etc. - // nest infinitely in a way that causes stack overflow (they don't check to avoid - // multiple wrappers). But they should be treated as immutable because they end - // up shared between multiple ResolveContext. - this.resolveStack = resolveStack; - this.cycleMarkers = cycleMarkers; - } - - private static Set newCycleMarkers() { - return Collections.newSetFromMap(new IdentityHashMap()); - } - - ResolveContext(ConfigResolveOptions options, Path restrictToChild) { - // LinkedHashSet keeps the traversal order which is at least useful - // in error messages if nothing else - this(new ResolveMemos(), options, restrictToChild, new ArrayList(), newCycleMarkers()); - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), "ResolveContext restrict to child " + restrictToChild); - } - - ResolveContext addCycleMarker(com.gu.typesafe.config.impl.AbstractConfigValue value) { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), "++ Cycle marker " + value + "@" + System.identityHashCode(value)); - if (cycleMarkers.contains(value)) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Added cycle marker twice " + value); - Set copy = newCycleMarkers(); - copy.addAll(cycleMarkers); - copy.add(value); - return new ResolveContext(memos, options, restrictToChild, resolveStack, copy); - } - - ResolveContext removeCycleMarker(com.gu.typesafe.config.impl.AbstractConfigValue value) { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), "-- Cycle marker " + value + "@" + System.identityHashCode(value)); - - Set copy = newCycleMarkers(); - copy.addAll(cycleMarkers); - copy.remove(value); - return new ResolveContext(memos, options, restrictToChild, resolveStack, copy); - } - - private ResolveContext memoize(MemoKey key, com.gu.typesafe.config.impl.AbstractConfigValue value) { - ResolveMemos changed = memos.put(key, value); - return new ResolveContext(changed, options, restrictToChild, resolveStack, cycleMarkers); - } - - ConfigResolveOptions options() { - return options; - } - - boolean isRestrictedToChild() { - return restrictToChild != null; - } - - Path restrictToChild() { - return restrictToChild; - } - - // restrictTo may be null to unrestrict - ResolveContext restrict(Path restrictTo) { - if (restrictTo == restrictToChild) - return this; - else - return new ResolveContext(memos, options, restrictTo, resolveStack, cycleMarkers); - } - - ResolveContext unrestricted() { - return restrict(null); - } - - String traceString() { - String separator = ", "; - StringBuilder sb = new StringBuilder(); - for (com.gu.typesafe.config.impl.AbstractConfigValue value : resolveStack) { - if (value instanceof ConfigReference) { - sb.append(((ConfigReference) value).expression().toString()); - sb.append(separator); - } - } - if (sb.length() > 0) - sb.setLength(sb.length() - separator.length()); - return sb.toString(); - } - - private ResolveContext pushTrace(com.gu.typesafe.config.impl.AbstractConfigValue value) { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), "pushing trace " + value); - List copy = new ArrayList(resolveStack); - copy.add(value); - return new ResolveContext(memos, options, restrictToChild, copy, cycleMarkers); - } - - ResolveContext popTrace() { - List copy = new ArrayList(resolveStack); - com.gu.typesafe.config.impl.AbstractConfigValue old = copy.remove(resolveStack.size() - 1); - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth() - 1, "popped trace " + old); - return new ResolveContext(memos, options, restrictToChild, copy, cycleMarkers); - } - - int depth() { - if (resolveStack.size() > 30) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("resolve getting too deep"); - return resolveStack.size(); - } - - com.gu.typesafe.config.impl.ResolveResult resolve(com.gu.typesafe.config.impl.AbstractConfigValue original, ResolveSource source) - throws NotPossibleToResolve { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl - .trace(depth(), "resolving " + original + " restrictToChild=" + restrictToChild + " in " + source); - return pushTrace(original).realResolve(original, source).popTrace(); - } - - private com.gu.typesafe.config.impl.ResolveResult realResolve(com.gu.typesafe.config.impl.AbstractConfigValue original, ResolveSource source) - throws NotPossibleToResolve { - // a fully-resolved (no restrictToChild) object can satisfy a - // request for a restricted object, so always check that first. - final MemoKey fullKey = new MemoKey(original, null); - MemoKey restrictedKey = null; - - com.gu.typesafe.config.impl.AbstractConfigValue cached = memos.get(fullKey); - - // but if there was no fully-resolved object cached, we'll only - // compute the restrictToChild object so use a more limited - // memo key - if (cached == null && isRestrictedToChild()) { - restrictedKey = new MemoKey(original, restrictToChild()); - cached = memos.get(restrictedKey); - } - - if (cached != null) { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), "using cached resolution " + cached + " for " + original - + " restrictToChild " + restrictToChild()); - return com.gu.typesafe.config.impl.ResolveResult.make(this, cached); - } else { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), - "not found in cache, resolving " + original + "@" + System.identityHashCode(original)); - - if (cycleMarkers.contains(original)) { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), - "Cycle detected, can't resolve; " + original + "@" + System.identityHashCode(original)); - throw new NotPossibleToResolve(this); - } - - com.gu.typesafe.config.impl.ResolveResult result = original.resolveSubstitutions(this, source); - com.gu.typesafe.config.impl.AbstractConfigValue resolved = result.value; - - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), "resolved to " + resolved + "@" + System.identityHashCode(resolved) - + " from " + original + "@" + System.identityHashCode(resolved)); - - ResolveContext withMemo = result.context; - - if (resolved == null || resolved.resolveStatus() == ResolveStatus.RESOLVED) { - // if the resolved object is fully resolved by resolving - // only the restrictToChildOrNull, then it can be cached - // under fullKey since the child we were restricted to - // turned out to be the only unresolved thing. - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), "caching " + fullKey + " result " + resolved); - - withMemo = withMemo.memoize(fullKey, resolved); - } else { - // if we have an unresolved object then either we did a - // partial resolve restricted to a certain child, or we are - // allowing incomplete resolution, or it's a bug. - if (isRestrictedToChild()) { - if (restrictedKey == null) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "restrictedKey should not be null here"); - } - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), "caching " + restrictedKey + " result " + resolved); - - withMemo = withMemo.memoize(restrictedKey, resolved); - } else if (options().getAllowUnresolved()) { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(depth(), "caching " + fullKey + " result " + resolved); - - withMemo = withMemo.memoize(fullKey, resolved); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "resolveSubstitutions() did not give us a resolved object"); - } - } - - return com.gu.typesafe.config.impl.ResolveResult.make(withMemo, resolved); - } - } - - static com.gu.typesafe.config.impl.AbstractConfigValue resolve(com.gu.typesafe.config.impl.AbstractConfigValue value, AbstractConfigObject root, - ConfigResolveOptions options) { - ResolveSource source = new ResolveSource(root); - ResolveContext context = new ResolveContext(options, null /* restrictToChild */); - - try { - return context.resolve(value, source).value; - } catch (NotPossibleToResolve e) { - // ConfigReference was supposed to catch NotPossibleToResolve - throw new ConfigException.BugOrBroken( - "NotPossibleToResolve was thrown from an outermost resolve", e); - } - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveMemos.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveMemos.java deleted file mode 100644 index bfe2bd994a..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveMemos.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.gu.typesafe.config.impl; - -/** - * This exists because we have to memoize resolved substitutions as we go - * through the config tree; otherwise we could end up creating multiple copies - * of values or whole trees of values as we follow chains of substitutions. - */ -final class ResolveMemos { - // note that we can resolve things to undefined (represented as Java null, - // rather than ConfigNull) so this map can have null values. - final private BadMap memos; - - private ResolveMemos(BadMap memos) { - this.memos = memos; - } - - ResolveMemos() { - this(new BadMap<>()); - } - - com.gu.typesafe.config.impl.AbstractConfigValue get(MemoKey key) { - return memos.get(key); - } - - ResolveMemos put(MemoKey key, com.gu.typesafe.config.impl.AbstractConfigValue value) { - return new ResolveMemos(memos.copyingPut(key, value)); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveResult.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveResult.java deleted file mode 100644 index db28246b27..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveResult.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigException; - -// value is allowed to be null -final class ResolveResult { - public final ResolveContext context; - public final V value; - - private ResolveResult(ResolveContext context, V value) { - this.context = context; - this.value = value; - } - - static ResolveResult make(ResolveContext context, V value) { - return new ResolveResult(context, value); - } - - // better option? we don't have variance - @SuppressWarnings("unchecked") - ResolveResult asObjectResult() { - if (!(value instanceof AbstractConfigObject)) - throw new ConfigException.BugOrBroken("Expecting a resolve result to be an object, but it was " + value); - Object o = this; - return (ResolveResult) o; - } - - // better option? we don't have variance - @SuppressWarnings("unchecked") - ResolveResult asValueResult() { - Object o = this; - return (ResolveResult) o; - } - - ResolveResult popTrace() { - return make(context.popTrace(), value); - } - - @Override - public String toString() { - return "ResolveResult(" + value + ")"; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveSource.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveSource.java deleted file mode 100644 index d18d088c63..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveSource.java +++ /dev/null @@ -1,350 +0,0 @@ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.impl.AbstractConfigValue.NotPossibleToResolve; - -/** - * This class is the source for values for a substitution like ${foo}. - */ -final class ResolveSource { - - final AbstractConfigObject root; - // This is used for knowing the chain of parents we used to get here. - // null if we should assume we are not a descendant of the root. - // the root itself should be a node in this if non-null. - final Node pathFromRoot; - - ResolveSource(AbstractConfigObject root, Node pathFromRoot) { - this.root = root; - this.pathFromRoot = pathFromRoot; - } - - ResolveSource(AbstractConfigObject root) { - this.root = root; - this.pathFromRoot = null; - } - - // if we replace the root with a non-object, use an empty - // object with nothing in it instead. - private AbstractConfigObject rootMustBeObj(Container value) { - if (value instanceof AbstractConfigObject) { - return (AbstractConfigObject) value; - } else { - return SimpleConfigObject.empty(); - } - } - - // as a side effect, findInObject() will have to resolve all parents of the - // child being peeked, but NOT the child itself. Caller has to resolve - // the child itself if needed. ValueWithPath.value can be null but - // the ValueWithPath instance itself should not be. - static private ResultWithPath findInObject(AbstractConfigObject obj, com.gu.typesafe.config.impl.ResolveContext context, Path path) - throws NotPossibleToResolve { - // resolve ONLY portions of the object which are along our path - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace("*** finding '" + path + "' in " + obj); - Path restriction = context.restrictToChild(); - com.gu.typesafe.config.impl.ResolveResult partiallyResolved = context.restrict(path).resolve(obj, - new ResolveSource(obj)); - com.gu.typesafe.config.impl.ResolveContext newContext = partiallyResolved.context.restrict(restriction); - if (partiallyResolved.value instanceof AbstractConfigObject) { - ValueWithPath pair = findInObject((AbstractConfigObject) partiallyResolved.value, path); - return new ResultWithPath(com.gu.typesafe.config.impl.ResolveResult.make(newContext, pair.value), pair.pathFromRoot); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("resolved object to non-object " + obj + " to " + partiallyResolved); - } - } - - static private ValueWithPath findInObject(AbstractConfigObject obj, Path path) { - try { - // we'll fail if anything along the path can't - // be looked at without resolving. - return findInObject(obj, path, null); - } catch (com.gu.typesafe.config.ConfigException.NotResolved e) { - throw ConfigImpl.improveNotResolved(path, e); - } - } - - static private ValueWithPath findInObject(AbstractConfigObject obj, Path path, Node parents) { - String key = path.first(); - Path next = path.remainder(); - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace("*** looking up '" + key + "' in " + obj); - com.gu.typesafe.config.impl.AbstractConfigValue v = obj.attemptPeekWithPartialResolve(key); - Node newParents = parents == null ? new Node(obj) : parents.prepend(obj); - - if (next == null) { - return new ValueWithPath(v, newParents); - } else { - if (v instanceof AbstractConfigObject) { - return findInObject((AbstractConfigObject) v, next, newParents); - } else { - return new ValueWithPath(null, newParents); - } - } - } - - ResultWithPath lookupSubst(com.gu.typesafe.config.impl.ResolveContext context, SubstitutionExpression subst, - int prefixLength) - throws NotPossibleToResolve { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(context.depth(), "searching for " + subst); - - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(context.depth(), subst + " - looking up relative to file it occurred in"); - // First we look up the full path, which means relative to the - // included file if we were not a root file - ResultWithPath result = findInObject(root, context, subst.path()); - - if (result.result.value == null) { - // Then we want to check relative to the root file. We don't - // want the prefix we were included at to be used when looking - // up env variables either. - Path unprefixed = subst.path().subPath(prefixLength); - - if (prefixLength > 0) { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(result.result.context.depth(), unprefixed - + " - looking up relative to parent file"); - result = findInObject(root, result.result.context, unprefixed); - } - - if (result.result.value == null && result.result.context.options().getUseSystemEnvironment()) { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(result.result.context.depth(), unprefixed + " - looking up in system environment"); - result = findInObject(ConfigImpl.envVariablesAsConfigObject(), context, unprefixed); - } - } - - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace(result.result.context.depth(), "resolved to " + result); - - return result; - } - - ResolveSource pushParent(Container parent) { - if (parent == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("can't push null parent"); - - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace("pushing parent " + parent + " ==root " + (parent == root) + " onto " + this); - - if (pathFromRoot == null) { - if (parent == root) { - return new ResolveSource(root, new Node(parent)); - } else { - if (ConfigImpl.traceSubstitutionsEnabled()) { - // this hasDescendant check is super-expensive so it's a - // trace message rather than an assertion - if (root.hasDescendant((com.gu.typesafe.config.impl.AbstractConfigValue) parent)) - ConfigImpl.trace("***** BUG ***** tried to push parent " + parent - + " without having a path to it in " + this); - } - // ignore parents if we aren't proceeding from the - // root - return this; - } - } else { - Container parentParent = pathFromRoot.head(); - if (ConfigImpl.traceSubstitutionsEnabled()) { - // this hasDescendant check is super-expensive so it's a - // trace message rather than an assertion - if (parentParent != null && !parentParent.hasDescendant((com.gu.typesafe.config.impl.AbstractConfigValue) parent)) - ConfigImpl.trace("***** BUG ***** trying to push non-child of " + parentParent + ", non-child was " - + parent); - } - - return new ResolveSource(root, pathFromRoot.prepend(parent)); - } - } - - ResolveSource resetParents() { - if (pathFromRoot == null) - return this; - else - return new ResolveSource(root); - } - - // returns null if the replacement results in deleting all the nodes. - private static Node replace(Node list, Container old, com.gu.typesafe.config.impl.AbstractConfigValue replacement) { - Container child = list.head(); - if (child != old) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Can only replace() the top node we're resolving; had " + child - + " on top and tried to replace " + old + " overall list was " + list); - Container parent = list.tail() == null ? null : list.tail().head(); - if (replacement == null || !(replacement instanceof Container)) { - if (parent == null) { - return null; - } else { - /* - * we are deleting the child from the stack of containers - * because it's either going away or not a container - */ - com.gu.typesafe.config.impl.AbstractConfigValue newParent = parent.replaceChild((com.gu.typesafe.config.impl.AbstractConfigValue) old, null); - - return replace(list.tail(), parent, newParent); - } - } else { - /* we replaced the container with another container */ - if (parent == null) { - return new Node((Container) replacement); - } else { - com.gu.typesafe.config.impl.AbstractConfigValue newParent = parent.replaceChild((com.gu.typesafe.config.impl.AbstractConfigValue) old, replacement); - Node newTail = replace(list.tail(), parent, newParent); - if (newTail != null) - return newTail.prepend((Container) replacement); - else - return new Node((Container) replacement); - } - } - } - - ResolveSource replaceCurrentParent(Container old, Container replacement) { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace("replaceCurrentParent old " + old + "@" + System.identityHashCode(old) + " replacement " - + replacement + "@" + System.identityHashCode(old) + " in " + this); - if (old == replacement) { - return this; - } else if (pathFromRoot != null) { - Node newPath = replace(pathFromRoot, old, (com.gu.typesafe.config.impl.AbstractConfigValue) replacement); - if (ConfigImpl.traceSubstitutionsEnabled()) { - ConfigImpl.trace("replaced " + old + " with " + replacement + " in " + this); - ConfigImpl.trace("path was: " + pathFromRoot + " is now " + newPath); - } - // if we end up nuking the root object itself, we replace it with an - // empty root - if (newPath != null) - return new ResolveSource((AbstractConfigObject) newPath.last(), newPath); - else - return new ResolveSource(SimpleConfigObject.empty()); - } else { - if (old == root) { - return new ResolveSource(rootMustBeObj(replacement)); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("attempt to replace root " + root + " with " + replacement); - // return this; - } - } - } - - // replacement may be null to delete - ResolveSource replaceWithinCurrentParent(com.gu.typesafe.config.impl.AbstractConfigValue old, com.gu.typesafe.config.impl.AbstractConfigValue replacement) { - if (ConfigImpl.traceSubstitutionsEnabled()) - ConfigImpl.trace("replaceWithinCurrentParent old " + old + "@" + System.identityHashCode(old) - + " replacement " + replacement + "@" + System.identityHashCode(old) + " in " + this); - if (old == replacement) { - return this; - } else if (pathFromRoot != null) { - Container parent = pathFromRoot.head(); - com.gu.typesafe.config.impl.AbstractConfigValue newParent = parent.replaceChild(old, replacement); - return replaceCurrentParent(parent, (newParent instanceof Container) ? (Container) newParent : null); - } else { - if (old == root && replacement instanceof Container) { - return new ResolveSource(rootMustBeObj((Container) replacement)); - } else { - throw new ConfigException.BugOrBroken("replace in parent not possible " + old + " with " + replacement - + " in " + this); - // return this; - } - } - } - - @Override - public String toString() { - return "ResolveSource(root=" + root + ", pathFromRoot=" + pathFromRoot + ")"; - } - - // a persistent list - static final class Node { - final T value; - final Node next; - - Node(T value, Node next) { - this.value = value; - this.next = next; - } - - Node(T value) { - this(value, null); - } - - Node prepend(T value) { - return new Node(value, this); - } - - T head() { - return value; - } - - Node tail() { - return next; - } - - T last() { - Node i = this; - while (i.next != null) - i = i.next; - return i.value; - } - - Node reverse() { - if (next == null) { - return this; - } else { - Node reversed = new Node(value); - Node i = next; - while (i != null) { - reversed = reversed.prepend(i.value); - i = i.next; - } - return reversed; - } - } - - @Override - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("["); - Node toAppendValue = this.reverse(); - while (toAppendValue != null) { - sb.append(toAppendValue.value.toString()); - if (toAppendValue.next != null) - sb.append(" <= "); - toAppendValue = toAppendValue.next; - } - sb.append("]"); - return sb.toString(); - } - } - - // value is allowed to be null - static final class ValueWithPath { - final com.gu.typesafe.config.impl.AbstractConfigValue value; - final Node pathFromRoot; - - ValueWithPath(com.gu.typesafe.config.impl.AbstractConfigValue value, Node pathFromRoot) { - this.value = value; - this.pathFromRoot = pathFromRoot; - } - - @Override - public String toString() { - return "ValueWithPath(value=" + value + ", pathFromRoot=" + pathFromRoot + ")"; - } - } - - static final class ResultWithPath { - final com.gu.typesafe.config.impl.ResolveResult result; - final Node pathFromRoot; - - ResultWithPath(com.gu.typesafe.config.impl.ResolveResult result, Node pathFromRoot) { - this.result = result; - this.pathFromRoot = pathFromRoot; - } - - @Override - public String toString() { - return "ResultWithPath(result=" + result + ", pathFromRoot=" + pathFromRoot + ")"; - } - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveStatus.java b/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveStatus.java deleted file mode 100644 index 6c9c707e5d..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/ResolveStatus.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.Collection; - -/** - * Status of substitution resolution. - */ -enum ResolveStatus { - UNRESOLVED, RESOLVED; - - final static ResolveStatus fromValues( - Collection values) { - for (com.gu.typesafe.config.impl.AbstractConfigValue v : values) { - if (v.resolveStatus() == ResolveStatus.UNRESOLVED) - return ResolveStatus.UNRESOLVED; - } - return ResolveStatus.RESOLVED; - } - - final static ResolveStatus fromBoolean(boolean resolved) { - return resolved ? ResolveStatus.RESOLVED : ResolveStatus.UNRESOLVED; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/SerializedConfigValue.java b/scripts/src/main/java/com/gu/typesafe/config/impl/SerializedConfigValue.java deleted file mode 100644 index 3a58150ae8..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/SerializedConfigValue.java +++ /dev/null @@ -1,528 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.DataOutput; -import java.io.DataOutputStream; -import java.io.Externalizable; -import java.io.IOException; -import java.io.NotSerializableException; -import java.io.ObjectInput; -import java.io.ObjectOutput; -import java.io.ObjectStreamException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.gu.typesafe.config.*; - -/** - * Deliberately shoving all the serialization code into this class instead of - * doing it OO-style with each subclass. Seems better to have it all in one - * place. This class implements a lame serialization format that supports - * skipping unknown fields, so it's moderately more extensible than the default - * Java serialization format. - */ -class SerializedConfigValue extends com.gu.typesafe.config.impl.AbstractConfigValue implements Externalizable { - - // this is the version used by Java serialization, if it increments it's - // essentially an ABI break and bad - private static final long serialVersionUID = 1L; - - // this is how we try to be extensible - static enum SerializedField { - // represents a field code we didn't recognize - UNKNOWN, - - // end of a list of fields - END_MARKER, - - // Fields at the root - ROOT_VALUE, - ROOT_WAS_CONFIG, - - // Fields that make up a value - VALUE_DATA, - VALUE_ORIGIN, - - // Fields that make up an origin - ORIGIN_DESCRIPTION, - ORIGIN_LINE_NUMBER, - ORIGIN_END_LINE_NUMBER, - ORIGIN_TYPE, - ORIGIN_URL, - ORIGIN_COMMENTS, - ORIGIN_NULL_URL, - ORIGIN_NULL_COMMENTS, - ORIGIN_RESOURCE, - ORIGIN_NULL_RESOURCE; - - static SerializedField forInt(int b) { - if (b < values().length) - return values()[b]; - else - return UNKNOWN; - } - }; - - private static enum SerializedValueType { - // the ordinals here are in the wire format, caution - NULL(com.gu.typesafe.config.ConfigValueType.NULL), - BOOLEAN(com.gu.typesafe.config.ConfigValueType.BOOLEAN), - INT(com.gu.typesafe.config.ConfigValueType.NUMBER), - LONG(com.gu.typesafe.config.ConfigValueType.NUMBER), - DOUBLE(com.gu.typesafe.config.ConfigValueType.NUMBER), - STRING(com.gu.typesafe.config.ConfigValueType.STRING), - LIST(com.gu.typesafe.config.ConfigValueType.LIST), - OBJECT(com.gu.typesafe.config.ConfigValueType.OBJECT); - - com.gu.typesafe.config.ConfigValueType configType; - - SerializedValueType(com.gu.typesafe.config.ConfigValueType configType) { - this.configType = configType; - } - - static SerializedValueType forInt(int b) { - if (b < values().length) - return values()[b]; - else - return null; - } - - static SerializedValueType forValue(com.gu.typesafe.config.ConfigValue value) { - com.gu.typesafe.config.ConfigValueType t = value.valueType(); - if (t == com.gu.typesafe.config.ConfigValueType.NUMBER) { - if (value instanceof com.gu.typesafe.config.impl.ConfigInt) - return INT; - else if (value instanceof ConfigLong) - return LONG; - else if (value instanceof ConfigDouble) - return DOUBLE; - } else { - for (SerializedValueType st : values()) { - if (st.configType == t) - return st; - } - } - - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("don't know how to serialize " + value); - } - }; - - private com.gu.typesafe.config.ConfigValue value; - private boolean wasConfig; - - // this has to be public for the Java deserializer - public SerializedConfigValue() { - super(null); - } - - SerializedConfigValue(com.gu.typesafe.config.ConfigValue value) { - this(); - this.value = value; - this.wasConfig = false; - } - - SerializedConfigValue(Config conf) { - this(conf.root()); - this.wasConfig = true; - } - - // when Java deserializer reads this object, return the contained - // object instead. - private Object readResolve() throws ObjectStreamException { - if (wasConfig) - return ((com.gu.typesafe.config.ConfigObject) value).toConfig(); - else - return value; - } - - private static class FieldOut { - final SerializedField code; - final ByteArrayOutputStream bytes; - final DataOutput data; - - FieldOut(SerializedField code) { - this.code = code; - this.bytes = new ByteArrayOutputStream(); - this.data = new DataOutputStream(bytes); - } - } - - // this is a separate function to prevent bugs writing to the - // outer stream instead of field.data - private static void writeOriginField(DataOutput out, SerializedField code, Object v) - throws IOException { - switch (code) { - case ORIGIN_DESCRIPTION: - out.writeUTF((String) v); - break; - case ORIGIN_LINE_NUMBER: - out.writeInt((Integer) v); - break; - case ORIGIN_END_LINE_NUMBER: - out.writeInt((Integer) v); - break; - case ORIGIN_TYPE: - out.writeByte((Integer) v); - break; - case ORIGIN_URL: - out.writeUTF((String) v); - break; - case ORIGIN_RESOURCE: - out.writeUTF((String) v); - break; - case ORIGIN_COMMENTS: - @SuppressWarnings("unchecked") - List list = (List) v; - int size = list.size(); - out.writeInt(size); - for (String s : list) { - out.writeUTF(s); - } - break; - case ORIGIN_NULL_URL: // FALL THRU - case ORIGIN_NULL_RESOURCE: // FALL THRU - case ORIGIN_NULL_COMMENTS: - // nothing to write out besides code and length - break; - default: - throw new IOException("Unhandled field from origin: " + code); - } - } - - // not private because we use it to serialize ConfigException - static void writeOrigin(DataOutput out, SimpleConfigOrigin origin, - SimpleConfigOrigin baseOrigin) throws IOException { - Map m; - // to serialize a null origin, we write out no fields at all - if (origin != null) - m = origin.toFieldsDelta(baseOrigin); - else - m = Collections.emptyMap(); - for (Map.Entry e : m.entrySet()) { - FieldOut field = new FieldOut(e.getKey()); - Object v = e.getValue(); - writeOriginField(field.data, field.code, v); - writeField(out, field); - } - writeEndMarker(out); - } - - // not private because we use it to deserialize ConfigException - static SimpleConfigOrigin readOrigin(DataInput in, SimpleConfigOrigin baseOrigin) - throws IOException { - Map m = new EnumMap(SerializedField.class); - while (true) { - Object v = null; - SerializedField field = readCode(in); - switch (field) { - case END_MARKER: - return SimpleConfigOrigin.fromBase(baseOrigin, m); - case ORIGIN_DESCRIPTION: - in.readInt(); // discard length - v = in.readUTF(); - break; - case ORIGIN_LINE_NUMBER: - in.readInt(); // discard length - v = in.readInt(); - break; - case ORIGIN_END_LINE_NUMBER: - in.readInt(); // discard length - v = in.readInt(); - break; - case ORIGIN_TYPE: - in.readInt(); // discard length - v = in.readUnsignedByte(); - break; - case ORIGIN_URL: - in.readInt(); // discard length - v = in.readUTF(); - break; - case ORIGIN_RESOURCE: - in.readInt(); // discard length - v = in.readUTF(); - break; - case ORIGIN_COMMENTS: - in.readInt(); // discard length - int size = in.readInt(); - List list = new ArrayList(size); - for (int i = 0; i < size; ++i) { - list.add(in.readUTF()); - } - v = list; - break; - case ORIGIN_NULL_URL: // FALL THRU - case ORIGIN_NULL_RESOURCE: // FALL THRU - case ORIGIN_NULL_COMMENTS: - // nothing to read besides code and length - in.readInt(); // discard length - v = ""; // just something non-null to put in the map - break; - case ROOT_VALUE: - case ROOT_WAS_CONFIG: - case VALUE_DATA: - case VALUE_ORIGIN: - throw new IOException("Not expecting this field here: " + field); - case UNKNOWN: - // skip unknown field - skipField(in); - break; - } - if (v != null) - m.put(field, v); - } - } - - private static void writeValueData(DataOutput out, com.gu.typesafe.config.ConfigValue value) throws IOException { - SerializedValueType st = SerializedValueType.forValue(value); - out.writeByte(st.ordinal()); - switch (st) { - case BOOLEAN: - out.writeBoolean(((ConfigBoolean) value).unwrapped()); - break; - case NULL: - break; - case INT: - // saving numbers as both string and binary is redundant but easy - out.writeInt(((com.gu.typesafe.config.impl.ConfigInt) value).unwrapped()); - out.writeUTF(((ConfigNumber) value).transformToString()); - break; - case LONG: - out.writeLong(((ConfigLong) value).unwrapped()); - out.writeUTF(((ConfigNumber) value).transformToString()); - break; - case DOUBLE: - out.writeDouble(((ConfigDouble) value).unwrapped()); - out.writeUTF(((ConfigNumber) value).transformToString()); - break; - case STRING: - out.writeUTF(((ConfigString) value).unwrapped()); - break; - case LIST: - com.gu.typesafe.config.ConfigList list = (ConfigList) value; - out.writeInt(list.size()); - for (com.gu.typesafe.config.ConfigValue v : list) { - writeValue(out, v, (SimpleConfigOrigin) list.origin()); - } - break; - case OBJECT: - com.gu.typesafe.config.ConfigObject obj = (ConfigObject) value; - out.writeInt(obj.size()); - for (Map.Entry e : obj.entrySet()) { - out.writeUTF(e.getKey()); - writeValue(out, e.getValue(), (SimpleConfigOrigin) obj.origin()); - } - break; - } - } - - private static com.gu.typesafe.config.impl.AbstractConfigValue readValueData(DataInput in, SimpleConfigOrigin origin) - throws IOException { - int stb = in.readUnsignedByte(); - SerializedValueType st = SerializedValueType.forInt(stb); - if (st == null) - throw new IOException("Unknown serialized value type: " + stb); - switch (st) { - case BOOLEAN: - return new ConfigBoolean(origin, in.readBoolean()); - case NULL: - return new ConfigNull(origin); - case INT: - int vi = in.readInt(); - String si = in.readUTF(); - return new com.gu.typesafe.config.impl.ConfigInt(origin, vi, si); - case LONG: - long vl = in.readLong(); - String sl = in.readUTF(); - return new ConfigLong(origin, vl, sl); - case DOUBLE: - double vd = in.readDouble(); - String sd = in.readUTF(); - return new ConfigDouble(origin, vd, sd); - case STRING: - return new ConfigString.Quoted(origin, in.readUTF()); - case LIST: - int listSize = in.readInt(); - List list = new ArrayList(listSize); - for (int i = 0; i < listSize; ++i) { - com.gu.typesafe.config.impl.AbstractConfigValue v = readValue(in, origin); - list.add(v); - } - return new SimpleConfigList(origin, list); - case OBJECT: - int mapSize = in.readInt(); - Map map = new HashMap(mapSize); - for (int i = 0; i < mapSize; ++i) { - String key = in.readUTF(); - com.gu.typesafe.config.impl.AbstractConfigValue v = readValue(in, origin); - map.put(key, v); - } - return new SimpleConfigObject(origin, map); - } - throw new IOException("Unhandled serialized value type: " + st); - } - - private static void writeValue(DataOutput out, ConfigValue value, SimpleConfigOrigin baseOrigin) - throws IOException { - FieldOut origin = new FieldOut(SerializedField.VALUE_ORIGIN); - writeOrigin(origin.data, (SimpleConfigOrigin) value.origin(), - baseOrigin); - writeField(out, origin); - - FieldOut data = new FieldOut(SerializedField.VALUE_DATA); - writeValueData(data.data, value); - writeField(out, data); - - writeEndMarker(out); - } - - private static com.gu.typesafe.config.impl.AbstractConfigValue readValue(DataInput in, SimpleConfigOrigin baseOrigin) - throws IOException { - com.gu.typesafe.config.impl.AbstractConfigValue value = null; - SimpleConfigOrigin origin = null; - while (true) { - SerializedField code = readCode(in); - if (code == SerializedField.END_MARKER) { - if (value == null) - throw new IOException("No value data found in serialization of value"); - return value; - } else if (code == SerializedField.VALUE_DATA) { - if (origin == null) - throw new IOException("Origin must be stored before value data"); - in.readInt(); // discard length - value = readValueData(in, origin); - } else if (code == SerializedField.VALUE_ORIGIN) { - in.readInt(); // discard length - origin = readOrigin(in, baseOrigin); - } else { - // ignore unknown field - skipField(in); - } - } - } - - private static void writeField(DataOutput out, FieldOut field) throws IOException { - byte[] bytes = field.bytes.toByteArray(); - out.writeByte(field.code.ordinal()); - out.writeInt(bytes.length); - out.write(bytes); - } - - private static void writeEndMarker(DataOutput out) throws IOException { - out.writeByte(SerializedField.END_MARKER.ordinal()); - } - - private static SerializedField readCode(DataInput in) throws IOException { - int c = in.readUnsignedByte(); - if (c == SerializedField.UNKNOWN.ordinal()) - throw new IOException("field code " + c + " is not supposed to be on the wire"); - return SerializedField.forInt(c); - } - - private static void skipField(DataInput in) throws IOException { - int len = in.readInt(); - // skipBytes doesn't have to block - int skipped = in.skipBytes(len); - if (skipped < len) { - // wastefully use readFully() if skipBytes didn't work - byte[] bytes = new byte[(len - skipped)]; - in.readFully(bytes); - } - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - if (((com.gu.typesafe.config.impl.AbstractConfigValue) value).resolveStatus() != ResolveStatus.RESOLVED) - throw new NotSerializableException( - "tried to serialize a value with unresolved substitutions, need to Config#resolve() first, see API docs"); - FieldOut field = new FieldOut(SerializedField.ROOT_VALUE); - writeValue(field.data, value, null /* baseOrigin */); - writeField(out, field); - - field = new FieldOut(SerializedField.ROOT_WAS_CONFIG); - field.data.writeBoolean(wasConfig); - writeField(out, field); - - writeEndMarker(out); - } - - @Override - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - while (true) { - SerializedField code = readCode(in); - if (code == SerializedField.END_MARKER) { - return; - } - - DataInput input = fieldIn(in); - if (code == SerializedField.ROOT_VALUE) { - this.value = readValue(input, null /* baseOrigin */); - } else if (code == SerializedField.ROOT_WAS_CONFIG) { - this.wasConfig = input.readBoolean(); - } - } - } - - private DataInput fieldIn(ObjectInput in) throws IOException { - byte[] bytes = new byte[in.readInt()]; - in.readFully(bytes); - return new DataInputStream(new ByteArrayInputStream(bytes)); - } - - private static com.gu.typesafe.config.ConfigException shouldNotBeUsed() { - return new ConfigException.BugOrBroken(SerializedConfigValue.class.getName() - + " should not exist outside of serialization"); - } - - @Override - public ConfigValueType valueType() { - throw shouldNotBeUsed(); - } - - @Override - public Object unwrapped() { - throw shouldNotBeUsed(); - } - - @Override - protected SerializedConfigValue newCopy(ConfigOrigin origin) { - throw shouldNotBeUsed(); - } - - @Override - public final String toString() { - return getClass().getSimpleName() + "(value=" + value + ",wasConfig=" + wasConfig + ")"; - } - - @Override - public boolean equals(Object other) { - // there's no reason we will ever call this equals(), but - // the one in AbstractConfigValue would explode due to - // calling unwrapped() above, so we just give some - // safe-to-call implementation to avoid breaking the - // contract of java.lang.Object - if (other instanceof SerializedConfigValue) { - return canEqual(other) - && (this.wasConfig == ((SerializedConfigValue) other).wasConfig) - && (this.value.equals(((SerializedConfigValue) other).value)); - } else { - return false; - } - } - - @Override - public int hashCode() { - int h = 41 * (41 + value.hashCode()); - h = 41 * (h + (wasConfig ? 1 : 0)); - return h; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfig.java b/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfig.java deleted file mode 100644 index dc9562b8bf..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfig.java +++ /dev/null @@ -1,1185 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.ObjectStreamException; -import java.io.Serializable; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.time.DateTimeException; -import java.time.Duration; -import java.time.Period; -import java.time.temporal.ChronoUnit; -import java.time.temporal.TemporalAmount; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import com.gu.typesafe.config.*; - -/** - * One thing to keep in mind in the future: as Collection-like APIs are added - * here, including iterators or size() or anything, they should be consistent - * with a one-level java.util.Map from paths to non-null values. Null values are - * not "in" the map. - */ -final class SimpleConfig implements com.gu.typesafe.config.Config, MergeableValue, Serializable { - - private static final long serialVersionUID = 1L; - - final private AbstractConfigObject object; - - SimpleConfig(AbstractConfigObject object) { - this.object = object; - } - - @Override - public AbstractConfigObject root() { - return object; - } - - @Override - public com.gu.typesafe.config.ConfigOrigin origin() { - return object.origin(); - } - - @Override - public SimpleConfig resolve() { - return resolve(com.gu.typesafe.config.ConfigResolveOptions.defaults()); - } - - @Override - public SimpleConfig resolve(com.gu.typesafe.config.ConfigResolveOptions options) { - return resolveWith(this, options); - } - - @Override - public SimpleConfig resolveWith(com.gu.typesafe.config.Config source) { - return resolveWith(source, com.gu.typesafe.config.ConfigResolveOptions.defaults()); - } - - @Override - public SimpleConfig resolveWith(com.gu.typesafe.config.Config source, ConfigResolveOptions options) { - com.gu.typesafe.config.impl.AbstractConfigValue resolved = ResolveContext.resolve(object, ((SimpleConfig) source).object, options); - - if (resolved == object) - return this; - else - return new SimpleConfig((AbstractConfigObject) resolved); - } - - private com.gu.typesafe.config.ConfigValue hasPathPeek(String pathExpression) { - Path path = Path.newPath(pathExpression); - com.gu.typesafe.config.ConfigValue peeked; - try { - peeked = object.peekPath(path); - } catch (com.gu.typesafe.config.ConfigException.NotResolved e) { - throw ConfigImpl.improveNotResolved(path, e); - } - return peeked; - } - - @Override - public boolean hasPath(String pathExpression) { - com.gu.typesafe.config.ConfigValue peeked = hasPathPeek(pathExpression); - return peeked != null && peeked.valueType() != com.gu.typesafe.config.ConfigValueType.NULL; - } - - @Override - public boolean hasPathOrNull(String path) { - com.gu.typesafe.config.ConfigValue peeked = hasPathPeek(path); - return peeked != null; - } - - @Override - public boolean isEmpty() { - return object.isEmpty(); - } - - private static void findPaths(Set> entries, Path parent, - AbstractConfigObject obj) { - for (Map.Entry entry : obj.entrySet()) { - String elem = entry.getKey(); - com.gu.typesafe.config.ConfigValue v = entry.getValue(); - Path path = Path.newKey(elem); - if (parent != null) - path = path.prepend(parent); - if (v instanceof AbstractConfigObject) { - findPaths(entries, path, (AbstractConfigObject) v); - } else if (v instanceof com.gu.typesafe.config.impl.ConfigNull) { - // nothing; nulls are conceptually not in a Config - } else { - entries.add(new AbstractMap.SimpleImmutableEntry(path.render(), v)); - } - } - } - - @Override - public Set> entrySet() { - Set> entries = new HashSet>(); - findPaths(entries, null, object); - return entries; - } - - static private com.gu.typesafe.config.impl.AbstractConfigValue throwIfNull(com.gu.typesafe.config.impl.AbstractConfigValue v, com.gu.typesafe.config.ConfigValueType expected, Path originalPath) { - if (v.valueType() == com.gu.typesafe.config.ConfigValueType.NULL) - throw new com.gu.typesafe.config.ConfigException.Null(v.origin(), originalPath.render(), - expected != null ? expected.name() : null); - else - return v; - } - - static private com.gu.typesafe.config.impl.AbstractConfigValue findKey(AbstractConfigObject self, String key, - com.gu.typesafe.config.ConfigValueType expected, Path originalPath) { - return throwIfNull(findKeyOrNull(self, key, expected, originalPath), expected, originalPath); - } - - static private com.gu.typesafe.config.impl.AbstractConfigValue findKeyOrNull(AbstractConfigObject self, String key, - com.gu.typesafe.config.ConfigValueType expected, Path originalPath) { - com.gu.typesafe.config.impl.AbstractConfigValue v = self.peekAssumingResolved(key, originalPath); - if (v == null) - throw new com.gu.typesafe.config.ConfigException.Missing(self.origin(), originalPath.render()); - - if (expected != null) - v = DefaultTransformer.transform(v, expected); - - if (expected != null && (v.valueType() != expected && v.valueType() != com.gu.typesafe.config.ConfigValueType.NULL)) - throw new com.gu.typesafe.config.ConfigException.WrongType(v.origin(), originalPath.render(), expected.name(), - v.valueType().name()); - else - return v; - } - - static private com.gu.typesafe.config.impl.AbstractConfigValue findOrNull(AbstractConfigObject self, Path path, - com.gu.typesafe.config.ConfigValueType expected, Path originalPath) { - try { - String key = path.first(); - Path next = path.remainder(); - if (next == null) { - return findKeyOrNull(self, key, expected, originalPath); - } else { - AbstractConfigObject o = (AbstractConfigObject) findKey(self, key, - com.gu.typesafe.config.ConfigValueType.OBJECT, - originalPath.subPath(0, originalPath.length() - next.length())); - assert (o != null); // missing was supposed to throw - return findOrNull(o, next, expected, originalPath); - } - } catch (com.gu.typesafe.config.ConfigException.NotResolved e) { - throw ConfigImpl.improveNotResolved(path, e); - } - } - - com.gu.typesafe.config.impl.AbstractConfigValue find(Path pathExpression, com.gu.typesafe.config.ConfigValueType expected, Path originalPath) { - return throwIfNull(findOrNull(object, pathExpression, expected, originalPath), expected, originalPath); - } - - com.gu.typesafe.config.impl.AbstractConfigValue find(String pathExpression, com.gu.typesafe.config.ConfigValueType expected) { - Path path = Path.newPath(pathExpression); - return find(path, expected, path); - } - - private com.gu.typesafe.config.impl.AbstractConfigValue findOrNull(Path pathExpression, com.gu.typesafe.config.ConfigValueType expected, Path originalPath) { - return findOrNull(object, pathExpression, expected, originalPath); - } - - private com.gu.typesafe.config.impl.AbstractConfigValue findOrNull(String pathExpression, com.gu.typesafe.config.ConfigValueType expected) { - Path path = Path.newPath(pathExpression); - return findOrNull(path, expected, path); - } - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue getValue(String path) { - return find(path, null); - } - - @Override - public boolean getIsNull(String path) { - com.gu.typesafe.config.impl.AbstractConfigValue v = findOrNull(path, null); - return (v.valueType() == com.gu.typesafe.config.ConfigValueType.NULL); - } - - @Override - public boolean getBoolean(String path) { - com.gu.typesafe.config.ConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.BOOLEAN); - return (Boolean) v.unwrapped(); - } - - private com.gu.typesafe.config.impl.ConfigNumber getConfigNumber(String path) { - com.gu.typesafe.config.ConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.NUMBER); - return (com.gu.typesafe.config.impl.ConfigNumber) v; - } - - @Override - public Number getNumber(String path) { - return getConfigNumber(path).unwrapped(); - } - - @Override - public int getInt(String path) { - com.gu.typesafe.config.impl.ConfigNumber n = getConfigNumber(path); - return n.intValueRangeChecked(path); - } - - @Override - public long getLong(String path) { - return getNumber(path).longValue(); - } - - @Override - public double getDouble(String path) { - return getNumber(path).doubleValue(); - } - - @Override - public String getString(String path) { - com.gu.typesafe.config.ConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.STRING); - return (String) v.unwrapped(); - } - - @Override - public > T getEnum(Class enumClass, String path) { - com.gu.typesafe.config.ConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.STRING); - return getEnumValue(path, enumClass, v); - } - - @Override - public com.gu.typesafe.config.ConfigList getList(String path) { - com.gu.typesafe.config.impl.AbstractConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.LIST); - return (ConfigList) v; - } - - @Override - public AbstractConfigObject getObject(String path) { - AbstractConfigObject obj = (AbstractConfigObject) find(path, com.gu.typesafe.config.ConfigValueType.OBJECT); - return obj; - } - - @Override - public SimpleConfig getConfig(String path) { - return getObject(path).toConfig(); - } - - @Override - public Object getAnyRef(String path) { - com.gu.typesafe.config.ConfigValue v = find(path, null); - return v.unwrapped(); - } - - @Override - public Long getBytes(String path) { - BigInteger bytes = getBytesBigInteger(path); - com.gu.typesafe.config.ConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.STRING); - return toLong(bytes, v.origin(), path); - } - - private BigInteger getBytesBigInteger(String path) { - BigInteger bytes; - com.gu.typesafe.config.ConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.STRING); - try { - bytes = BigInteger.valueOf(getLong(path)); - } catch (com.gu.typesafe.config.ConfigException.WrongType e) { - bytes = parseBytes((String) v.unwrapped(), - v.origin(), path); - } - if (bytes.signum() < 0) - throw new com.gu.typesafe.config.ConfigException.BadValue(v.origin(), path, - "Attempt to construct memory size with negative number: " + bytes); - return bytes; - } - - private List getBytesListBigInteger(String path){ - List result = new ArrayList<>(); - List list = getList(path); - - for (com.gu.typesafe.config.ConfigValue v : list) { - BigInteger bytes; - if (v.valueType() == com.gu.typesafe.config.ConfigValueType.NUMBER) { - bytes = BigInteger.valueOf(((Number) v.unwrapped()).longValue()); - } else if (v.valueType() == com.gu.typesafe.config.ConfigValueType.STRING) { - String s = (String) v.unwrapped(); - bytes = parseBytes(s, v.origin(), path); - } else { - throw new com.gu.typesafe.config.ConfigException.WrongType(v.origin(), path, - "memory size string or number of bytes", v.valueType() - .name()); - } - if (bytes.signum() < 0) - throw new com.gu.typesafe.config.ConfigException.BadValue(v.origin(), path, - "Attempt to construct ConfigMemorySize with negative number: " + bytes); - - result.add(bytes); - } - return result; - } - - @Override - public com.gu.typesafe.config.ConfigMemorySize getMemorySize(String path) { - return com.gu.typesafe.config.ConfigMemorySize.ofBytes(getBytesBigInteger(path)); - } - - @Deprecated - @Override - public Long getMilliseconds(String path) { - return getDuration(path, TimeUnit.MILLISECONDS); - } - - @Deprecated - @Override - public Long getNanoseconds(String path) { - return getDuration(path, TimeUnit.NANOSECONDS); - } - - @Override - public long getDuration(String path, TimeUnit unit) { - com.gu.typesafe.config.ConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.STRING); - long result = unit.convert( - parseDuration((String) v.unwrapped(), v.origin(), path), - TimeUnit.NANOSECONDS); - return result; - } - - @Override - public Duration getDuration(String path) { - com.gu.typesafe.config.ConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.STRING); - long nanos = parseDuration((String) v.unwrapped(), v.origin(), path); - return Duration.ofNanos(nanos); - } - - @Override - public Period getPeriod(String path){ - com.gu.typesafe.config.ConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.STRING); - return parsePeriod((String) v.unwrapped(), v.origin(), path); - } - - @Override - public TemporalAmount getTemporal(String path){ - try{ - return getDuration(path); - } catch (com.gu.typesafe.config.ConfigException.BadValue e){ - return getPeriod(path); - } - } - - @SuppressWarnings("unchecked") - private List getHomogeneousUnwrappedList(String path, - com.gu.typesafe.config.ConfigValueType expected) { - List l = new ArrayList(); - List list = getList(path); - for (com.gu.typesafe.config.ConfigValue cv : list) { - // variance would be nice, but stupid cast will do - com.gu.typesafe.config.impl.AbstractConfigValue v = (com.gu.typesafe.config.impl.AbstractConfigValue) cv; - if (expected != null) { - v = DefaultTransformer.transform(v, expected); - } - if (v.valueType() != expected) - throw new com.gu.typesafe.config.ConfigException.WrongType(v.origin(), path, - "list of " + expected.name(), "list of " - + v.valueType().name()); - l.add((T) v.unwrapped()); - } - return l; - } - - @Override - public List getBooleanList(String path) { - return getHomogeneousUnwrappedList(path, com.gu.typesafe.config.ConfigValueType.BOOLEAN); - } - - @Override - public List getNumberList(String path) { - return getHomogeneousUnwrappedList(path, com.gu.typesafe.config.ConfigValueType.NUMBER); - } - - @Override - public List getIntList(String path) { - List l = new ArrayList(); - List numbers = getHomogeneousWrappedList(path, com.gu.typesafe.config.ConfigValueType.NUMBER); - for (com.gu.typesafe.config.impl.AbstractConfigValue v : numbers) { - l.add(((com.gu.typesafe.config.impl.ConfigNumber) v).intValueRangeChecked(path)); - } - return l; - } - - @Override - public List getLongList(String path) { - List l = new ArrayList(); - List numbers = getNumberList(path); - for (Number n : numbers) { - l.add(n.longValue()); - } - return l; - } - - @Override - public List getDoubleList(String path) { - List l = new ArrayList(); - List numbers = getNumberList(path); - for (Number n : numbers) { - l.add(n.doubleValue()); - } - return l; - } - - @Override - public List getStringList(String path) { - return getHomogeneousUnwrappedList(path, com.gu.typesafe.config.ConfigValueType.STRING); - } - - @Override - public > List getEnumList(Class enumClass, String path) { - List enumNames = getHomogeneousWrappedList(path, com.gu.typesafe.config.ConfigValueType.STRING); - List enumList = new ArrayList(); - for (ConfigString enumName : enumNames) { - enumList.add(getEnumValue(path, enumClass, enumName)); - } - return enumList; - } - - private > T getEnumValue(String path, Class enumClass, com.gu.typesafe.config.ConfigValue enumConfigValue) { - String enumName = (String) enumConfigValue.unwrapped(); - try { - return Enum.valueOf(enumClass, enumName); - } catch (IllegalArgumentException e) { - List enumNames = new ArrayList(); - Enum[] enumConstants = enumClass.getEnumConstants(); - if (enumConstants != null) { - for (Enum enumConstant : enumConstants) { - enumNames.add(enumConstant.name()); - } - } - throw new com.gu.typesafe.config.ConfigException.BadValue( - enumConfigValue.origin(), path, - String.format("The enum class %s has no constant of the name '%s' (should be one of %s.)", - enumClass.getSimpleName(), enumName, enumNames)); - } - } - - @SuppressWarnings("unchecked") - private List getHomogeneousWrappedList( - String path, com.gu.typesafe.config.ConfigValueType expected) { - List l = new ArrayList(); - List list = getList(path); - for (com.gu.typesafe.config.ConfigValue cv : list) { - // variance would be nice, but stupid cast will do - com.gu.typesafe.config.impl.AbstractConfigValue v = (com.gu.typesafe.config.impl.AbstractConfigValue) cv; - if (expected != null) { - v = DefaultTransformer.transform(v, expected); - } - if (v.valueType() != expected) - throw new com.gu.typesafe.config.ConfigException.WrongType(v.origin(), path, - "list of " + expected.name(), "list of " - + v.valueType().name()); - l.add((T) v); - } - return l; - } - - @Override - public List getObjectList(String path) { - return getHomogeneousWrappedList(path, com.gu.typesafe.config.ConfigValueType.OBJECT); - } - - @Override - public List getConfigList(String path) { - List objects = getObjectList(path); - List l = new ArrayList(); - for (ConfigObject o : objects) { - l.add(o.toConfig()); - } - return l; - } - - @Override - public List getAnyRefList(String path) { - List l = new ArrayList(); - List list = getList(path); - for (com.gu.typesafe.config.ConfigValue v : list) { - l.add(v.unwrapped()); - } - return l; - } - - @Override - public List getBytesList(String path) { - com.gu.typesafe.config.ConfigValue v = find(path, com.gu.typesafe.config.ConfigValueType.LIST); - return getBytesListBigInteger(path).stream() - .map(bytes -> toLong(bytes, v.origin(), path)) - .collect(Collectors.toList()); - } - - private Long toLong(BigInteger value, com.gu.typesafe.config.ConfigOrigin originForException, - String pathForException){ - if (value.bitLength() < 64) { - return value.longValue(); - } else { - throw new com.gu.typesafe.config.ConfigException.BadValue(originForException, pathForException, - "size-in-bytes value is out of range for a 64-bit long: '" + value + "'"); - } - } - - @Override - public List getMemorySizeList(String path) { - return getBytesListBigInteger(path).stream() - .map(ConfigMemorySize::ofBytes) - .collect(Collectors.toList()); - } - - @Override - public List getDurationList(String path, TimeUnit unit) { - List l = new ArrayList(); - List list = getList(path); - for (com.gu.typesafe.config.ConfigValue v : list) { - if (v.valueType() == com.gu.typesafe.config.ConfigValueType.NUMBER) { - Long n = unit.convert( - ((Number) v.unwrapped()).longValue(), - TimeUnit.MILLISECONDS); - l.add(n); - } else if (v.valueType() == com.gu.typesafe.config.ConfigValueType.STRING) { - String s = (String) v.unwrapped(); - Long n = unit.convert( - parseDuration(s, v.origin(), path), - TimeUnit.NANOSECONDS); - l.add(n); - } else { - throw new com.gu.typesafe.config.ConfigException.WrongType(v.origin(), path, - "duration string or number of milliseconds", - v.valueType().name()); - } - } - return l; - } - - @Override - public List getDurationList(String path) { - List l = getDurationList(path, TimeUnit.NANOSECONDS); - List builder = new ArrayList(l.size()); - for (Long value : l) { - builder.add(Duration.ofNanos(value)); - } - return builder; - } - - @Deprecated - @Override - public List getMillisecondsList(String path) { - return getDurationList(path, TimeUnit.MILLISECONDS); - } - - @Deprecated - @Override - public List getNanosecondsList(String path) { - return getDurationList(path, TimeUnit.NANOSECONDS); - } - - @Override - public AbstractConfigObject toFallbackValue() { - return object; - } - - @Override - public SimpleConfig withFallback(ConfigMergeable other) { - // this can return "this" if the withFallback doesn't need a new - // ConfigObject - return object.withFallback(other).toConfig(); - } - - @Override - public final boolean equals(Object other) { - if (other instanceof SimpleConfig) { - return object.equals(((SimpleConfig) other).object); - } else { - return false; - } - } - - @Override - public final int hashCode() { - // we do the "41*" just so our hash code won't match that of the - // underlying object. there's no real reason it can't match, but - // making it not match might catch some kinds of bug. - return 41 * object.hashCode(); - } - - @Override - public String toString() { - return "Config(" + object.toString() + ")"; - } - - private static String getUnits(String s) { - int i = s.length() - 1; - while (i >= 0) { - char c = s.charAt(i); - if (!Character.isLetter(c)) - break; - i -= 1; - } - return s.substring(i + 1); - } - - /** - * Parses a period string. If no units are specified in the string, it is - * assumed to be in days. The returned period is in days. - * The purpose of this function is to implement the period-related methods - * in the ConfigObject interface. - * - * @param input - * the string to parse - * @param originForException - * origin of the value being parsed - * @param pathForException - * path to include in exceptions - * @return duration in days - * @throws com.gu.typesafe.config.ConfigException - * if string is invalid - */ - public static Period parsePeriod(String input, - com.gu.typesafe.config.ConfigOrigin originForException, String pathForException) { - String s = ConfigImplUtil.unicodeTrim(input); - String originalUnitString = getUnits(s); - String unitString = originalUnitString; - String numberString = ConfigImplUtil.unicodeTrim(s.substring(0, s.length() - - unitString.length())); - ChronoUnit units; - - // this would be caught later anyway, but the error message - // is more helpful if we check it here. - if (numberString.length() == 0) - throw new com.gu.typesafe.config.ConfigException.BadValue(originForException, - pathForException, "No number in period value '" + input - + "'"); - - if (unitString.length() > 2 && !unitString.endsWith("s")) - unitString = unitString + "s"; - - // note that this is deliberately case-sensitive - if (unitString.equals("") || unitString.equals("d") || unitString.equals("days")) { - units = ChronoUnit.DAYS; - - } else if (unitString.equals("w") || unitString.equals("weeks")) { - units = ChronoUnit.WEEKS; - - } else if (unitString.equals("m") || unitString.equals("mo") || unitString.equals("months")) { - units = ChronoUnit.MONTHS; - - } else if (unitString.equals("y") || unitString.equals("years")) { - units = ChronoUnit.YEARS; - - } else { - throw new com.gu.typesafe.config.ConfigException.BadValue(originForException, - pathForException, "Could not parse time unit '" - + originalUnitString - + "' (try d, w, mo, y)"); - } - - try { - return periodOf(Integer.parseInt(numberString), units); - } catch (NumberFormatException e) { - throw new com.gu.typesafe.config.ConfigException.BadValue(originForException, - pathForException, "Could not parse duration number '" - + numberString + "'"); - } - } - - - private static Period periodOf(int n, ChronoUnit unit){ - if(unit.isTimeBased()){ - throw new DateTimeException(unit + " cannot be converted to a java.time.Period"); - } - - switch (unit){ - case DAYS: - return Period.ofDays(n); - case WEEKS: - return Period.ofWeeks(n); - case MONTHS: - return Period.ofMonths(n); - case YEARS: - return Period.ofYears(n); - default: - throw new DateTimeException(unit + " cannot be converted to a java.time.Period"); - } - } - - /** - * Parses a duration string. If no units are specified in the string, it is - * assumed to be in milliseconds. The returned duration is in nanoseconds. - * The purpose of this function is to implement the duration-related methods - * in the ConfigObject interface. - * - * @param input - * the string to parse - * @param originForException - * origin of the value being parsed - * @param pathForException - * path to include in exceptions - * @return duration in nanoseconds - * @throws com.gu.typesafe.config.ConfigException - * if string is invalid - */ - public static long parseDuration(String input, - com.gu.typesafe.config.ConfigOrigin originForException, String pathForException) { - String s = ConfigImplUtil.unicodeTrim(input); - String originalUnitString = getUnits(s); - String unitString = originalUnitString; - String numberString = ConfigImplUtil.unicodeTrim(s.substring(0, s.length() - - unitString.length())); - TimeUnit units = null; - - // this would be caught later anyway, but the error message - // is more helpful if we check it here. - if (numberString.length() == 0) - throw new com.gu.typesafe.config.ConfigException.BadValue(originForException, - pathForException, "No number in duration value '" + input - + "'"); - - if (unitString.length() > 2 && !unitString.endsWith("s")) - unitString = unitString + "s"; - - // note that this is deliberately case-sensitive - if (unitString.equals("") || unitString.equals("ms") || unitString.equals("millis") - || unitString.equals("milliseconds")) { - units = TimeUnit.MILLISECONDS; - } else if (unitString.equals("us") || unitString.equals("micros") || unitString.equals("microseconds")) { - units = TimeUnit.MICROSECONDS; - } else if (unitString.equals("ns") || unitString.equals("nanos") || unitString.equals("nanoseconds")) { - units = TimeUnit.NANOSECONDS; - } else if (unitString.equals("d") || unitString.equals("days")) { - units = TimeUnit.DAYS; - } else if (unitString.equals("h") || unitString.equals("hours")) { - units = TimeUnit.HOURS; - } else if (unitString.equals("s") || unitString.equals("seconds")) { - units = TimeUnit.SECONDS; - } else if (unitString.equals("m") || unitString.equals("minutes")) { - units = TimeUnit.MINUTES; - } else { - throw new com.gu.typesafe.config.ConfigException.BadValue(originForException, - pathForException, "Could not parse time unit '" - + originalUnitString - + "' (try ns, us, ms, s, m, h, d)"); - } - - try { - // if the string is purely digits, parse as an integer to avoid - // possible precision loss; - // otherwise as a double. - if (numberString.matches("[+-]?[0-9]+")) { - return units.toNanos(Long.parseLong(numberString)); - } else { - long nanosInUnit = units.toNanos(1); - return (long) (Double.parseDouble(numberString) * nanosInUnit); - } - } catch (NumberFormatException e) { - throw new com.gu.typesafe.config.ConfigException.BadValue(originForException, - pathForException, "Could not parse duration number '" - + numberString + "'"); - } - } - - private static enum MemoryUnit { - BYTES("", 1024, 0), - - KILOBYTES("kilo", 1000, 1), - MEGABYTES("mega", 1000, 2), - GIGABYTES("giga", 1000, 3), - TERABYTES("tera", 1000, 4), - PETABYTES("peta", 1000, 5), - EXABYTES("exa", 1000, 6), - ZETTABYTES("zetta", 1000, 7), - YOTTABYTES("yotta", 1000, 8), - - KIBIBYTES("kibi", 1024, 1), - MEBIBYTES("mebi", 1024, 2), - GIBIBYTES("gibi", 1024, 3), - TEBIBYTES("tebi", 1024, 4), - PEBIBYTES("pebi", 1024, 5), - EXBIBYTES("exbi", 1024, 6), - ZEBIBYTES("zebi", 1024, 7), - YOBIBYTES("yobi", 1024, 8); - - final String prefix; - final int powerOf; - final int power; - final BigInteger bytes; - - MemoryUnit(String prefix, int powerOf, int power) { - this.prefix = prefix; - this.powerOf = powerOf; - this.power = power; - this.bytes = BigInteger.valueOf(powerOf).pow(power); - } - - private static Map makeUnitsMap() { - Map map = new HashMap(); - for (MemoryUnit unit : MemoryUnit.values()) { - map.put(unit.prefix + "byte", unit); - map.put(unit.prefix + "bytes", unit); - if (unit.prefix.length() == 0) { - map.put("b", unit); - map.put("B", unit); - map.put("", unit); // no unit specified means bytes - } else { - String first = unit.prefix.substring(0, 1); - String firstUpper = first.toUpperCase(); - if (unit.powerOf == 1024) { - map.put(first, unit); // 512m - map.put(firstUpper, unit); // 512M - map.put(firstUpper + "i", unit); // 512Mi - map.put(firstUpper + "iB", unit); // 512MiB - } else if (unit.powerOf == 1000) { - if (unit.power == 1) { - map.put(first + "B", unit); // 512kB - } else { - map.put(firstUpper + "B", unit); // 512MB - } - } else { - throw new RuntimeException("broken MemoryUnit enum"); - } - } - } - return map; - } - - private static Map unitsMap = makeUnitsMap(); - - static MemoryUnit parseUnit(String unit) { - return unitsMap.get(unit); - } - } - - /** - * Parses a size-in-bytes string. If no units are specified in the string, - * it is assumed to be in bytes. The returned value is in bytes. The purpose - * of this function is to implement the size-in-bytes-related methods in the - * Config interface. - * - * @param input - * the string to parse - * @param originForException - * origin of the value being parsed - * @param pathForException - * path to include in exceptions - * @return size in bytes - * @throws com.gu.typesafe.config.ConfigException - * if string is invalid - */ - public static BigInteger parseBytes(String input, com.gu.typesafe.config.ConfigOrigin originForException, - String pathForException) { - String s = ConfigImplUtil.unicodeTrim(input); - String unitString = getUnits(s); - String numberString = ConfigImplUtil.unicodeTrim(s.substring(0, - s.length() - unitString.length())); - - // this would be caught later anyway, but the error message - // is more helpful if we check it here. - if (numberString.length() == 0) - throw new com.gu.typesafe.config.ConfigException.BadValue(originForException, - pathForException, "No number in size-in-bytes value '" - + input + "'"); - - MemoryUnit units = MemoryUnit.parseUnit(unitString); - - if (units == null) { - throw new com.gu.typesafe.config.ConfigException.BadValue(originForException, pathForException, - "Could not parse size-in-bytes unit '" + unitString - + "' (try k, K, kB, KiB, kilobytes, kibibytes)"); - } - - try { - BigInteger result; - // if the string is purely digits, parse as an integer to avoid - // possible precision loss; otherwise as a double. - if (numberString.matches("[0-9]+")) { - result = units.bytes.multiply(new BigInteger(numberString)); - } else { - BigDecimal resultDecimal = (new BigDecimal(units.bytes)).multiply(new BigDecimal(numberString)); - result = resultDecimal.toBigInteger(); - } - return result; - } catch (NumberFormatException e) { - throw new com.gu.typesafe.config.ConfigException.BadValue(originForException, pathForException, - "Could not parse size-in-bytes number '" + numberString + "'"); - } - } - - private com.gu.typesafe.config.impl.AbstractConfigValue peekPath(Path path) { - return root().peekPath(path); - } - - private static void addProblem(List accumulator, Path path, - com.gu.typesafe.config.ConfigOrigin origin, String problem) { - accumulator.add(new com.gu.typesafe.config.ConfigException.ValidationProblem(path.render(), origin, problem)); - } - - private static String getDesc(com.gu.typesafe.config.ConfigValueType type) { - return type.name().toLowerCase(); - } - - private static String getDesc(com.gu.typesafe.config.ConfigValue refValue) { - if (refValue instanceof AbstractConfigObject) { - AbstractConfigObject obj = (AbstractConfigObject) refValue; - if (!obj.isEmpty()) - return "object with keys " + obj.keySet(); - else - return getDesc(refValue.valueType()); - } else { - return getDesc(refValue.valueType()); - } - } - - private static void addMissing(List accumulator, - String refDesc, Path path, com.gu.typesafe.config.ConfigOrigin origin) { - addProblem(accumulator, path, origin, "No setting at '" + path.render() + "', expecting: " - + refDesc); - } - - private static void addMissing(List accumulator, - com.gu.typesafe.config.ConfigValue refValue, Path path, com.gu.typesafe.config.ConfigOrigin origin) { - addMissing(accumulator, getDesc(refValue), path, origin); - } - - // JavaBean stuff uses this - static void addMissing(List accumulator, - com.gu.typesafe.config.ConfigValueType refType, Path path, com.gu.typesafe.config.ConfigOrigin origin) { - addMissing(accumulator, getDesc(refType), path, origin); - } - - private static void addWrongType(List accumulator, - String refDesc, com.gu.typesafe.config.impl.AbstractConfigValue actual, Path path) { - addProblem(accumulator, path, actual.origin(), "Wrong value type at '" + path.render() - + "', expecting: " + refDesc + " but got: " - + getDesc(actual)); - } - - private static void addWrongType(List accumulator, - com.gu.typesafe.config.ConfigValue refValue, com.gu.typesafe.config.impl.AbstractConfigValue actual, Path path) { - addWrongType(accumulator, getDesc(refValue), actual, path); - } - - private static void addWrongType(List accumulator, - com.gu.typesafe.config.ConfigValueType refType, com.gu.typesafe.config.impl.AbstractConfigValue actual, Path path) { - addWrongType(accumulator, getDesc(refType), actual, path); - } - - private static boolean couldBeNull(com.gu.typesafe.config.impl.AbstractConfigValue v) { - return DefaultTransformer.transform(v, com.gu.typesafe.config.ConfigValueType.NULL) - .valueType() == com.gu.typesafe.config.ConfigValueType.NULL; - } - - private static boolean haveCompatibleTypes(com.gu.typesafe.config.ConfigValue reference, com.gu.typesafe.config.impl.AbstractConfigValue value) { - if (couldBeNull((com.gu.typesafe.config.impl.AbstractConfigValue) reference)) { - // we allow any setting to be null - return true; - } else { - return haveCompatibleTypes(reference.valueType(), value); - } - } - - private static boolean haveCompatibleTypes(com.gu.typesafe.config.ConfigValueType referenceType, com.gu.typesafe.config.impl.AbstractConfigValue value) { - if (referenceType == com.gu.typesafe.config.ConfigValueType.NULL || couldBeNull(value)) { - // we allow any setting to be null - return true; - } else if (referenceType == com.gu.typesafe.config.ConfigValueType.OBJECT) { - if (value instanceof AbstractConfigObject) { - return true; - } else { - return false; - } - } else if (referenceType == com.gu.typesafe.config.ConfigValueType.LIST) { - // objects may be convertible to lists if they have numeric keys - if (value instanceof SimpleConfigList || value instanceof SimpleConfigObject) { - return true; - } else { - return false; - } - } else if (referenceType == com.gu.typesafe.config.ConfigValueType.STRING) { - // assume a string could be gotten as any non-collection type; - // allows things like getMilliseconds including domain-specific - // interpretations of strings - return true; - } else if (value instanceof ConfigString) { - // assume a string could be gotten as any non-collection type - return true; - } else { - if (referenceType == value.valueType()) { - return true; - } else { - return false; - } - } - } - - // path is null if we're at the root - private static void checkValidObject(Path path, AbstractConfigObject reference, - AbstractConfigObject value, - List accumulator) { - for (Map.Entry entry : reference.entrySet()) { - String key = entry.getKey(); - - Path childPath; - if (path != null) - childPath = Path.newKey(key).prepend(path); - else - childPath = Path.newKey(key); - - com.gu.typesafe.config.impl.AbstractConfigValue v = value.get(key); - if (v == null) { - addMissing(accumulator, entry.getValue(), childPath, value.origin()); - } else { - checkValid(childPath, entry.getValue(), v, accumulator); - } - } - } - - private static void checkListCompatibility(Path path, SimpleConfigList listRef, - SimpleConfigList listValue, List accumulator) { - if (listRef.isEmpty() || listValue.isEmpty()) { - // can't verify type, leave alone - } else { - com.gu.typesafe.config.impl.AbstractConfigValue refElement = listRef.get(0); - for (com.gu.typesafe.config.ConfigValue elem : listValue) { - com.gu.typesafe.config.impl.AbstractConfigValue e = (com.gu.typesafe.config.impl.AbstractConfigValue) elem; - if (!haveCompatibleTypes(refElement, e)) { - addProblem(accumulator, path, e.origin(), "List at '" + path.render() - + "' contains wrong value type, expecting list of " - + getDesc(refElement) + " but got element of type " + getDesc(e)); - // don't add a problem for every last array element - break; - } - } - } - } - - // Used by the JavaBean-based validator - static void checkValid(Path path, com.gu.typesafe.config.ConfigValueType referenceType, com.gu.typesafe.config.impl.AbstractConfigValue value, - List accumulator) { - if (haveCompatibleTypes(referenceType, value)) { - if (referenceType == com.gu.typesafe.config.ConfigValueType.LIST && value instanceof SimpleConfigObject) { - // attempt conversion of indexed object to list - com.gu.typesafe.config.impl.AbstractConfigValue listValue = DefaultTransformer.transform(value, - com.gu.typesafe.config.ConfigValueType.LIST); - if (!(listValue instanceof SimpleConfigList)) - addWrongType(accumulator, referenceType, value, path); - } - } else { - addWrongType(accumulator, referenceType, value, path); - } - } - - private static void checkValid(Path path, com.gu.typesafe.config.ConfigValue reference, com.gu.typesafe.config.impl.AbstractConfigValue value, - List accumulator) { - // Unmergeable is supposed to be impossible to encounter in here - // because we check for resolve status up front. - - if (haveCompatibleTypes(reference, value)) { - if (reference instanceof AbstractConfigObject && value instanceof AbstractConfigObject) { - checkValidObject(path, (AbstractConfigObject) reference, - (AbstractConfigObject) value, accumulator); - } else if (reference instanceof SimpleConfigList && value instanceof SimpleConfigList) { - SimpleConfigList listRef = (SimpleConfigList) reference; - SimpleConfigList listValue = (SimpleConfigList) value; - checkListCompatibility(path, listRef, listValue, accumulator); - } else if (reference instanceof SimpleConfigList && value instanceof SimpleConfigObject) { - // attempt conversion of indexed object to list - SimpleConfigList listRef = (SimpleConfigList) reference; - com.gu.typesafe.config.impl.AbstractConfigValue listValue = DefaultTransformer.transform(value, - ConfigValueType.LIST); - if (listValue instanceof SimpleConfigList) - checkListCompatibility(path, listRef, (SimpleConfigList) listValue, accumulator); - else - addWrongType(accumulator, reference, value, path); - } - } else { - addWrongType(accumulator, reference, value, path); - } - } - - @Override - public boolean isResolved() { - return root().resolveStatus() == ResolveStatus.RESOLVED; - } - - @Override - public void checkValid(com.gu.typesafe.config.Config reference, String... restrictToPaths) { - SimpleConfig ref = (SimpleConfig) reference; - - // unresolved reference config is a bug in the caller of checkValid - if (ref.root().resolveStatus() != ResolveStatus.RESOLVED) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "do not call checkValid() with an unresolved reference config, call Config#resolve(), see Config#resolve() API docs"); - - // unresolved config under validation is a bug in something, - // NotResolved is a more specific subclass of BugOrBroken - if (root().resolveStatus() != ResolveStatus.RESOLVED) - throw new com.gu.typesafe.config.ConfigException.NotResolved( - "need to Config#resolve() each config before using it, see the API docs for Config#resolve()"); - - // Now we know that both reference and this config are resolved - - List problems = new ArrayList(); - - if (restrictToPaths.length == 0) { - checkValidObject(null, ref.root(), root(), problems); - } else { - for (String p : restrictToPaths) { - Path path = Path.newPath(p); - com.gu.typesafe.config.impl.AbstractConfigValue refValue = ref.peekPath(path); - if (refValue != null) { - com.gu.typesafe.config.impl.AbstractConfigValue child = peekPath(path); - if (child != null) { - checkValid(path, refValue, child, problems); - } else { - addMissing(problems, refValue, path, origin()); - } - } - } - } - - if (!problems.isEmpty()) { - throw new ConfigException.ValidationFailed(problems); - } - } - - @Override - public SimpleConfig withOnlyPath(String pathExpression) { - Path path = Path.newPath(pathExpression); - return new SimpleConfig(root().withOnlyPath(path)); - } - - @Override - public SimpleConfig withoutPath(String pathExpression) { - Path path = Path.newPath(pathExpression); - return new SimpleConfig(root().withoutPath(path)); - } - - @Override - public SimpleConfig withValue(String pathExpression, ConfigValue v) { - Path path = Path.newPath(pathExpression); - return new SimpleConfig(root().withValue(path, v)); - } - - SimpleConfig atKey(ConfigOrigin origin, String key) { - return root().atKey(origin, key); - } - - @Override - public SimpleConfig atKey(String key) { - return root().atKey(key); - } - - @Override - public Config atPath(String path) { - return root().atPath(path); - } - - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new com.gu.typesafe.config.impl.SerializedConfigValue(this); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigDocument.java b/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigDocument.java deleted file mode 100644 index 1a522ed323..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigDocument.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigRenderOptions; -import com.gu.typesafe.config.parser.ConfigDocument; - -import java.io.StringReader; -import java.util.Iterator; - -final class SimpleConfigDocument implements com.gu.typesafe.config.parser.ConfigDocument { - private ConfigNodeRoot configNodeTree; - private com.gu.typesafe.config.ConfigParseOptions parseOptions; - - SimpleConfigDocument(ConfigNodeRoot parsedNode, com.gu.typesafe.config.ConfigParseOptions parseOptions) { - configNodeTree = parsedNode; - this.parseOptions = parseOptions; - } - - @Override - public com.gu.typesafe.config.parser.ConfigDocument withValueText(String path, String newValue) { - if (newValue == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("null value for " + path + " passed to withValueText"); - SimpleConfigOrigin origin = SimpleConfigOrigin.newSimple("single value parsing"); - StringReader reader = new StringReader(newValue); - Iterator tokens = Tokenizer.tokenize(origin, reader, parseOptions.getSyntax()); - com.gu.typesafe.config.impl.AbstractConfigNodeValue parsedValue = ConfigDocumentParser.parseValue(tokens, origin, parseOptions); - reader.close(); - - return new SimpleConfigDocument(configNodeTree.setValue(path, parsedValue, parseOptions.getSyntax()), parseOptions); - } - - @Override - public com.gu.typesafe.config.parser.ConfigDocument withValue(String path, com.gu.typesafe.config.ConfigValue newValue) { - if (newValue == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("null value for " + path + " passed to withValue"); - com.gu.typesafe.config.ConfigRenderOptions options = ConfigRenderOptions.defaults(); - options = options.setOriginComments(false); - return withValueText(path, newValue.render(options).trim()); - } - - @Override - public com.gu.typesafe.config.parser.ConfigDocument withoutPath(String path) { - return new SimpleConfigDocument(configNodeTree.setValue(path, null, parseOptions.getSyntax()), parseOptions); - } - - @Override - public boolean hasPath(String path) { - return configNodeTree.hasValue(path); - } - - public String render() { - return configNodeTree.render(); - } - - @Override - public boolean equals(Object other) { - return other instanceof com.gu.typesafe.config.parser.ConfigDocument && render().equals(((ConfigDocument) other).render()); - } - - @Override - public int hashCode() { - return render().hashCode(); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigList.java b/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigList.java deleted file mode 100644 index b6cdb8d996..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigList.java +++ /dev/null @@ -1,460 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.ObjectStreamException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -import com.gu.typesafe.config.*; -import com.gu.typesafe.config.ConfigRenderOptions; - -final class SimpleConfigList extends com.gu.typesafe.config.impl.AbstractConfigValue implements ConfigList, com.gu.typesafe.config.impl.Container, Serializable { - - private static final long serialVersionUID = 2L; - - final private List value; - final private boolean resolved; - - SimpleConfigList(com.gu.typesafe.config.ConfigOrigin origin, List value) { - this(origin, value, ResolveStatus - .fromValues(value)); - } - - SimpleConfigList(com.gu.typesafe.config.ConfigOrigin origin, List value, - ResolveStatus status) { - super(origin); - this.value = value; - this.resolved = status == ResolveStatus.RESOLVED; - - // kind of an expensive debug check (makes this constructor pointless) - if (status != ResolveStatus.fromValues(value)) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "SimpleConfigList created with wrong resolve status: " + this); - } - - @Override - public com.gu.typesafe.config.ConfigValueType valueType() { - return ConfigValueType.LIST; - } - - @Override - public List unwrapped() { - List list = new ArrayList(); - for (com.gu.typesafe.config.impl.AbstractConfigValue v : value) { - list.add(v.unwrapped()); - } - return list; - } - - @Override - ResolveStatus resolveStatus() { - return ResolveStatus.fromBoolean(resolved); - } - - @Override - public SimpleConfigList replaceChild(com.gu.typesafe.config.impl.AbstractConfigValue child, com.gu.typesafe.config.impl.AbstractConfigValue replacement) { - List newList = replaceChildInList(value, child, replacement); - if (newList == null) { - return null; - } else { - // we use the constructor flavor that will recompute the resolve - // status - return new SimpleConfigList(origin(), newList); - } - } - - @Override - public boolean hasDescendant(com.gu.typesafe.config.impl.AbstractConfigValue descendant) { - return hasDescendantInList(value, descendant); - } - - private SimpleConfigList modify(NoExceptionsModifier modifier, ResolveStatus newResolveStatus) { - try { - return modifyMayThrow(modifier, newResolveStatus); - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("unexpected checked exception", e); - } - } - - private SimpleConfigList modifyMayThrow(Modifier modifier, ResolveStatus newResolveStatus) - throws Exception { - // lazy-create for optimization - List changed = null; - int i = 0; - for (com.gu.typesafe.config.impl.AbstractConfigValue v : value) { - com.gu.typesafe.config.impl.AbstractConfigValue modified = modifier.modifyChildMayThrow(null /* key */, v); - - // lazy-create the new list if required - if (changed == null && modified != v) { - changed = new ArrayList(); - for (int j = 0; j < i; ++j) { - changed.add(value.get(j)); - } - } - - // once the new list is created, all elements - // have to go in it. if modifyChild returned - // null, we drop that element. - if (changed != null && modified != null) { - changed.add(modified); - } - - i += 1; - } - - if (changed != null) { - if (newResolveStatus != null) { - return new SimpleConfigList(origin(), changed, newResolveStatus); - } else { - return new SimpleConfigList(origin(), changed); - } - } else { - return this; - } - } - - private static class ResolveModifier implements Modifier { - com.gu.typesafe.config.impl.ResolveContext context; - final com.gu.typesafe.config.impl.ResolveSource source; - ResolveModifier(com.gu.typesafe.config.impl.ResolveContext context, com.gu.typesafe.config.impl.ResolveSource source) { - this.context = context; - this.source = source; - } - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue modifyChildMayThrow(String key, com.gu.typesafe.config.impl.AbstractConfigValue v) - throws NotPossibleToResolve { - com.gu.typesafe.config.impl.ResolveResult result = context.resolve(v, source); - context = result.context; - return result.value; - } - } - - @Override - com.gu.typesafe.config.impl.ResolveResult resolveSubstitutions(com.gu.typesafe.config.impl.ResolveContext context, com.gu.typesafe.config.impl.ResolveSource source) - throws NotPossibleToResolve { - if (resolved) - return com.gu.typesafe.config.impl.ResolveResult.make(context, this); - - if (context.isRestrictedToChild()) { - // if a list restricts to a child path, then it has no child paths, - // so nothing to do. - return com.gu.typesafe.config.impl.ResolveResult.make(context, this); - } else { - try { - ResolveModifier modifier = new ResolveModifier(context, source.pushParent(this)); - SimpleConfigList value = modifyMayThrow(modifier, context.options().getAllowUnresolved() ? null : ResolveStatus.RESOLVED); - return com.gu.typesafe.config.impl.ResolveResult.make(modifier.context, value); - } catch (NotPossibleToResolve e) { - throw e; - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new ConfigException.BugOrBroken("unexpected checked exception", e); - } - } - } - - @Override - SimpleConfigList relativized(final Path prefix) { - return modify(new NoExceptionsModifier() { - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue modifyChild(String key, com.gu.typesafe.config.impl.AbstractConfigValue v) { - return v.relativized(prefix); - } - - }, resolveStatus()); - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof SimpleConfigList; - } - - @Override - public boolean equals(Object other) { - // note that "origin" is deliberately NOT part of equality - if (other instanceof SimpleConfigList) { - // optimization to avoid unwrapped() for two ConfigList - return canEqual(other) - && (value == ((SimpleConfigList) other).value || value.equals(((SimpleConfigList) other).value)); - } else { - return false; - } - } - - @Override - public int hashCode() { - // note that "origin" is deliberately NOT part of equality - return value.hashCode(); - } - - @Override - protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) { - if (value.isEmpty()) { - sb.append("[]"); - } else { - sb.append("["); - if (options.getFormatted()) - sb.append('\n'); - for (com.gu.typesafe.config.impl.AbstractConfigValue v : value) { - if (options.getOriginComments()) { - String[] lines = v.origin().description().split("\n"); - for (String l : lines) { - indent(sb, indent + 1, options); - sb.append('#'); - if (!l.isEmpty()) - sb.append(' '); - sb.append(l); - sb.append("\n"); - } - } - if (options.getComments()) { - for (String comment : v.origin().comments()) { - indent(sb, indent + 1, options); - sb.append("# "); - sb.append(comment); - sb.append("\n"); - } - } - indent(sb, indent + 1, options); - - v.render(sb, indent + 1, atRoot, options); - sb.append(","); - if (options.getFormatted()) - sb.append('\n'); - } - sb.setLength(sb.length() - 1); // chop or newline - if (options.getFormatted()) { - sb.setLength(sb.length() - 1); // also chop comma - sb.append('\n'); - indent(sb, indent, options); - } - sb.append("]"); - } - } - - @Override - public boolean contains(Object o) { - return value.contains(o); - } - - @Override - public boolean containsAll(Collection c) { - return value.containsAll(c); - } - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue get(int index) { - return value.get(index); - } - - @Override - public int indexOf(Object o) { - return value.indexOf(o); - } - - @Override - public boolean isEmpty() { - return value.isEmpty(); - } - - @Override - public Iterator iterator() { - final Iterator i = value.iterator(); - - return new Iterator() { - @Override - public boolean hasNext() { - return i.hasNext(); - } - - @Override - public com.gu.typesafe.config.ConfigValue next() { - return i.next(); - } - - @Override - public void remove() { - throw weAreImmutable("iterator().remove"); - } - }; - } - - @Override - public int lastIndexOf(Object o) { - return value.lastIndexOf(o); - } - - private static ListIterator wrapListIterator( - final ListIterator i) { - return new ListIterator() { - @Override - public boolean hasNext() { - return i.hasNext(); - } - - @Override - public com.gu.typesafe.config.ConfigValue next() { - return i.next(); - } - - @Override - public void remove() { - throw weAreImmutable("listIterator().remove"); - } - - @Override - public void add(com.gu.typesafe.config.ConfigValue arg0) { - throw weAreImmutable("listIterator().add"); - } - - @Override - public boolean hasPrevious() { - return i.hasPrevious(); - } - - @Override - public int nextIndex() { - return i.nextIndex(); - } - - @Override - public com.gu.typesafe.config.ConfigValue previous() { - return i.previous(); - } - - @Override - public int previousIndex() { - return i.previousIndex(); - } - - @Override - public void set(com.gu.typesafe.config.ConfigValue arg0) { - throw weAreImmutable("listIterator().set"); - } - }; - } - - @Override - public ListIterator listIterator() { - return wrapListIterator(value.listIterator()); - } - - @Override - public ListIterator listIterator(int index) { - return wrapListIterator(value.listIterator(index)); - } - - @Override - public int size() { - return value.size(); - } - - @Override - public List subList(int fromIndex, int toIndex) { - List list = new ArrayList(); - // yay bloat caused by lack of type variance - for (com.gu.typesafe.config.impl.AbstractConfigValue v : value.subList(fromIndex, toIndex)) { - list.add(v); - } - return list; - } - - @Override - public Object[] toArray() { - return value.toArray(); - } - - @Override - public T[] toArray(T[] a) { - return value.toArray(a); - } - - private static UnsupportedOperationException weAreImmutable(String method) { - return new UnsupportedOperationException( - "ConfigList is immutable, you can't call List.'" + method + "'"); - } - - @Override - public boolean add(com.gu.typesafe.config.ConfigValue e) { - throw weAreImmutable("add"); - } - - @Override - public void add(int index, com.gu.typesafe.config.ConfigValue element) { - throw weAreImmutable("add"); - } - - @Override - public boolean addAll(Collection c) { - throw weAreImmutable("addAll"); - } - - @Override - public boolean addAll(int index, Collection c) { - throw weAreImmutable("addAll"); - } - - @Override - public void clear() { - throw weAreImmutable("clear"); - } - - @Override - public boolean remove(Object o) { - throw weAreImmutable("remove"); - } - - @Override - public com.gu.typesafe.config.ConfigValue remove(int index) { - throw weAreImmutable("remove"); - } - - @Override - public boolean removeAll(Collection c) { - throw weAreImmutable("removeAll"); - } - - @Override - public boolean retainAll(Collection c) { - throw weAreImmutable("retainAll"); - } - - @Override - public com.gu.typesafe.config.ConfigValue set(int index, ConfigValue element) { - throw weAreImmutable("set"); - } - - @Override - protected SimpleConfigList newCopy(com.gu.typesafe.config.ConfigOrigin newOrigin) { - return new SimpleConfigList(newOrigin, value); - } - - final SimpleConfigList concatenate(SimpleConfigList other) { - com.gu.typesafe.config.ConfigOrigin combinedOrigin = com.gu.typesafe.config.impl.SimpleConfigOrigin.mergeOrigins(origin(), other.origin()); - List combined = new ArrayList(value.size() - + other.value.size()); - combined.addAll(value); - combined.addAll(other.value); - return new SimpleConfigList(combinedOrigin, combined); - } - - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new com.gu.typesafe.config.impl.SerializedConfigValue(this); - } - - @Override - public SimpleConfigList withOrigin(ConfigOrigin origin) { - return (SimpleConfigList) super.withOrigin(origin); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigObject.java b/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigObject.java deleted file mode 100644 index 42b04304b6..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigObject.java +++ /dev/null @@ -1,674 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - * THIS FILE HAS BEEN MODIFIED TO ADD SUPPORT FOR COMPACT KEYS - */ -package com.gu.typesafe.config.impl; - -import java.io.ObjectStreamException; -import java.io.Serializable; -import java.math.BigInteger; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.gu.typesafe.config.*; - -final class SimpleConfigObject extends AbstractConfigObject implements Serializable { - - private static final long serialVersionUID = 2L; - - // this map should never be modified - assume immutable - final private Map value; - final private boolean resolved; - final private boolean ignoresFallbacks; - - SimpleConfigObject(com.gu.typesafe.config.ConfigOrigin origin, - Map value, ResolveStatus status, - boolean ignoresFallbacks) { - super(origin); - if (value == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "creating config object with null map"); - this.value = value; - this.resolved = status == ResolveStatus.RESOLVED; - this.ignoresFallbacks = ignoresFallbacks; - - // Kind of an expensive debug check. Comment out? - if (status != ResolveStatus.fromValues(value.values())) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("Wrong resolved status on " + this); - } - - SimpleConfigObject(com.gu.typesafe.config.ConfigOrigin origin, - Map value) { - this(origin, value, ResolveStatus.fromValues(value.values()), false /* ignoresFallbacks */); - } - - @Override - public SimpleConfigObject withOnlyKey(String key) { - return withOnlyPath(Path.newKey(key)); - } - - @Override - public SimpleConfigObject withoutKey(String key) { - return withoutPath(Path.newKey(key)); - } - - // gets the object with only the path if the path - // exists, otherwise null if it doesn't. this ensures - // that if we have { a : { b : 42 } } and do - // withOnlyPath("a.b.c") that we don't keep an empty - // "a" object. - @Override - protected SimpleConfigObject withOnlyPathOrNull(Path path) { - String key = path.first(); - Path next = path.remainder(); - com.gu.typesafe.config.impl.AbstractConfigValue v = value.get(key); - - if (next != null) { - if (v != null && (v instanceof AbstractConfigObject)) { - v = ((AbstractConfigObject) v).withOnlyPathOrNull(next); - } else { - // if the path has more elements but we don't have an object, - // then the rest of the path does not exist. - v = null; - } - } - - if (v == null) { - return null; - } else { - return new SimpleConfigObject(origin(), Collections.singletonMap(key, v), - v.resolveStatus(), ignoresFallbacks); - } - } - - @Override - SimpleConfigObject withOnlyPath(Path path) { - SimpleConfigObject o = withOnlyPathOrNull(path); - if (o == null) { - return new SimpleConfigObject(origin(), - Collections. emptyMap(), ResolveStatus.RESOLVED, - ignoresFallbacks); - } else { - return o; - } - } - - @Override - SimpleConfigObject withoutPath(Path path) { - String key = path.first(); - Path next = path.remainder(); - com.gu.typesafe.config.impl.AbstractConfigValue v = value.get(key); - - if (v != null && next != null && v instanceof AbstractConfigObject) { - v = ((AbstractConfigObject) v).withoutPath(next); - Map updated = new HashMap( - value); - updated.put(key, v); - return new SimpleConfigObject(origin(), updated, ResolveStatus.fromValues(updated - .values()), ignoresFallbacks); - } else if (next != null || v == null) { - // can't descend, nothing to remove - return this; - } else { - Map smaller = new HashMap( - value.size() - 1); - for (Map.Entry old : value.entrySet()) { - if (!old.getKey().equals(key)) - smaller.put(old.getKey(), old.getValue()); - } - return new SimpleConfigObject(origin(), smaller, ResolveStatus.fromValues(smaller - .values()), ignoresFallbacks); - } - } - - @Override - public SimpleConfigObject withValue(String key, com.gu.typesafe.config.ConfigValue v) { - if (v == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "Trying to store null ConfigValue in a ConfigObject"); - - Map newMap; - if (value.isEmpty()) { - newMap = Collections.singletonMap(key, (com.gu.typesafe.config.impl.AbstractConfigValue) v); - } else { - newMap = new HashMap(value); - newMap.put(key, (com.gu.typesafe.config.impl.AbstractConfigValue) v); - } - - return new SimpleConfigObject(origin(), newMap, ResolveStatus.fromValues(newMap.values()), - ignoresFallbacks); - } - - @Override - SimpleConfigObject withValue(Path path, com.gu.typesafe.config.ConfigValue v) { - String key = path.first(); - Path next = path.remainder(); - - if (next == null) { - return withValue(key, v); - } else { - com.gu.typesafe.config.impl.AbstractConfigValue child = value.get(key); - if (child != null && child instanceof AbstractConfigObject) { - // if we have an object, add to it - return withValue(key, ((AbstractConfigObject) child).withValue(next, v)); - } else { - // as soon as we have a non-object, replace it entirely - com.gu.typesafe.config.impl.SimpleConfig subtree = ((com.gu.typesafe.config.impl.AbstractConfigValue) v).atPath( - com.gu.typesafe.config.impl.SimpleConfigOrigin.newSimple("withValue(" + next.render() + ")"), next); - return withValue(key, subtree.root()); - } - } - } - - @Override - protected com.gu.typesafe.config.impl.AbstractConfigValue attemptPeekWithPartialResolve(String key) { - return value.get(key); - } - - private SimpleConfigObject newCopy(ResolveStatus newStatus, com.gu.typesafe.config.ConfigOrigin newOrigin, - boolean newIgnoresFallbacks) { - return new SimpleConfigObject(newOrigin, value, newStatus, newIgnoresFallbacks); - } - - @Override - protected SimpleConfigObject newCopy(ResolveStatus newStatus, com.gu.typesafe.config.ConfigOrigin newOrigin) { - return newCopy(newStatus, newOrigin, ignoresFallbacks); - } - - @Override - protected SimpleConfigObject withFallbacksIgnored() { - if (ignoresFallbacks) - return this; - else - return newCopy(resolveStatus(), origin(), true /* ignoresFallbacks */); - } - - @Override - ResolveStatus resolveStatus() { - return ResolveStatus.fromBoolean(resolved); - } - - @Override - public SimpleConfigObject replaceChild(com.gu.typesafe.config.impl.AbstractConfigValue child, com.gu.typesafe.config.impl.AbstractConfigValue replacement) { - HashMap newChildren = new HashMap(value); - for (Map.Entry old : newChildren.entrySet()) { - if (old.getValue() == child) { - if (replacement != null) - old.setValue(replacement); - else - newChildren.remove(old.getKey()); - - return new SimpleConfigObject(origin(), newChildren, ResolveStatus.fromValues(newChildren.values()), - ignoresFallbacks); - } - } - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("SimpleConfigObject.replaceChild did not find " + child + " in " + this); - } - - @Override - public boolean hasDescendant(com.gu.typesafe.config.impl.AbstractConfigValue descendant) { - for (com.gu.typesafe.config.impl.AbstractConfigValue child : value.values()) { - if (child == descendant) - return true; - } - // now do the expensive search - for (com.gu.typesafe.config.impl.AbstractConfigValue child : value.values()) { - if (child instanceof com.gu.typesafe.config.impl.Container && ((com.gu.typesafe.config.impl.Container) child).hasDescendant(descendant)) - return true; - } - - return false; - } - - @Override - protected boolean ignoresFallbacks() { - return ignoresFallbacks; - } - - @Override - public Map unwrapped() { - Map m = new HashMap(); - for (Map.Entry e : value.entrySet()) { - m.put(e.getKey(), e.getValue().unwrapped()); - } - return m; - } - - @Override - protected SimpleConfigObject mergedWithObject(AbstractConfigObject abstractFallback) { - requireNotIgnoringFallbacks(); - - if (!(abstractFallback instanceof SimpleConfigObject)) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "should not be reached (merging non-SimpleConfigObject)"); - } - - SimpleConfigObject fallback = (SimpleConfigObject) abstractFallback; - - boolean changed = false; - boolean allResolved = true; - Map merged = new HashMap(); - Set allKeys = new HashSet(); - allKeys.addAll(this.keySet()); - allKeys.addAll(fallback.keySet()); - for (String key : allKeys) { - com.gu.typesafe.config.impl.AbstractConfigValue first = this.value.get(key); - com.gu.typesafe.config.impl.AbstractConfigValue second = fallback.value.get(key); - com.gu.typesafe.config.impl.AbstractConfigValue kept; - if (first == null) - kept = second; - else if (second == null) - kept = first; - else - kept = first.withFallback(second); - - merged.put(key, kept); - - if (first != kept) - changed = true; - - if (kept.resolveStatus() == ResolveStatus.UNRESOLVED) - allResolved = false; - } - - ResolveStatus newResolveStatus = ResolveStatus.fromBoolean(allResolved); - boolean newIgnoresFallbacks = fallback.ignoresFallbacks(); - - if (changed) - return new SimpleConfigObject(mergeOrigins(this, fallback), merged, newResolveStatus, - newIgnoresFallbacks); - else if (newResolveStatus != resolveStatus() || newIgnoresFallbacks != ignoresFallbacks()) - return newCopy(newResolveStatus, origin(), newIgnoresFallbacks); - else - return this; - } - - private SimpleConfigObject modify(NoExceptionsModifier modifier) { - try { - return modifyMayThrow(modifier); - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("unexpected checked exception", e); - } - } - - private SimpleConfigObject modifyMayThrow(Modifier modifier) throws Exception { - Map changes = null; - for (String k : keySet()) { - com.gu.typesafe.config.impl.AbstractConfigValue v = value.get(k); - // "modified" may be null, which means remove the child; - // to do that we put null in the "changes" map. - com.gu.typesafe.config.impl.AbstractConfigValue modified = modifier.modifyChildMayThrow(k, v); - if (modified != v) { - if (changes == null) - changes = new HashMap(); - changes.put(k, modified); - } - } - if (changes == null) { - return this; - } else { - Map modified = new HashMap(); - boolean sawUnresolved = false; - for (String k : keySet()) { - if (changes.containsKey(k)) { - com.gu.typesafe.config.impl.AbstractConfigValue newValue = changes.get(k); - if (newValue != null) { - modified.put(k, newValue); - if (newValue.resolveStatus() == ResolveStatus.UNRESOLVED) - sawUnresolved = true; - } else { - // remove this child; don't put it in the new map. - } - } else { - com.gu.typesafe.config.impl.AbstractConfigValue newValue = value.get(k); - modified.put(k, newValue); - if (newValue.resolveStatus() == ResolveStatus.UNRESOLVED) - sawUnresolved = true; - } - } - return new SimpleConfigObject(origin(), modified, - sawUnresolved ? ResolveStatus.UNRESOLVED : ResolveStatus.RESOLVED, - ignoresFallbacks()); - } - } - - private static final class ResolveModifier implements Modifier { - - final Path originalRestrict; - com.gu.typesafe.config.impl.ResolveContext context; - final com.gu.typesafe.config.impl.ResolveSource source; - - ResolveModifier(com.gu.typesafe.config.impl.ResolveContext context, com.gu.typesafe.config.impl.ResolveSource source) { - this.context = context; - this.source = source; - originalRestrict = context.restrictToChild(); - } - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue modifyChildMayThrow(String key, com.gu.typesafe.config.impl.AbstractConfigValue v) throws NotPossibleToResolve { - if (context.isRestrictedToChild()) { - if (key.equals(context.restrictToChild().first())) { - Path remainder = context.restrictToChild().remainder(); - if (remainder != null) { - com.gu.typesafe.config.impl.ResolveResult result = context.restrict(remainder).resolve(v, - source); - context = result.context.unrestricted().restrict(originalRestrict); - return result.value; - } else { - // we don't want to resolve the leaf child. - return v; - } - } else { - // not in the restrictToChild path - return v; - } - } else { - // no restrictToChild, resolve everything - com.gu.typesafe.config.impl.ResolveResult result = context.unrestricted().resolve(v, source); - context = result.context.unrestricted().restrict(originalRestrict); - return result.value; - } - } - - } - - @Override - com.gu.typesafe.config.impl.ResolveResult resolveSubstitutions(com.gu.typesafe.config.impl.ResolveContext context, com.gu.typesafe.config.impl.ResolveSource source) - throws NotPossibleToResolve { - if (resolveStatus() == ResolveStatus.RESOLVED) - return com.gu.typesafe.config.impl.ResolveResult.make(context, this); - - final com.gu.typesafe.config.impl.ResolveSource sourceWithParent = source.pushParent(this); - - try { - ResolveModifier modifier = new ResolveModifier(context, sourceWithParent); - - com.gu.typesafe.config.impl.AbstractConfigValue value = modifyMayThrow(modifier); - return com.gu.typesafe.config.impl.ResolveResult.make(modifier.context, value).asObjectResult(); - } catch (NotPossibleToResolve e) { - throw e; - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new ConfigException.BugOrBroken("unexpected checked exception", e); - } - } - - @Override - SimpleConfigObject relativized(final Path prefix) { - return modify(new NoExceptionsModifier() { - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue modifyChild(String key, com.gu.typesafe.config.impl.AbstractConfigValue v) { - return v.relativized(prefix); - } - - }); - } - - // this is only Serializable to chill out a findbugs warning - static final private class RenderComparator implements java.util.Comparator, Serializable { - private static final long serialVersionUID = 1L; - - private static boolean isAllDigits(String s) { - int length = s.length(); - - // empty string doesn't count as a number - // string longer than "max number of digits in a long" cannot be parsed as a long - if (length == 0) - return false; - - for (int i = 0; i < length; ++i) { - char c = s.charAt(i); - - if (!Character.isDigit(c)) - return false; - } - return true; - } - - // This is supposed to sort numbers before strings, - // and sort the numbers numerically. The point is - // to make objects which are really list-like - // (numeric indices) appear in order. - @Override - public int compare(String a, String b) { - boolean aDigits = isAllDigits(a); - boolean bDigits = isAllDigits(b); - if (aDigits && bDigits) { - return new BigInteger(a).compareTo(new BigInteger(b)); - } else if (aDigits) { - return -1; - } else if (bDigits) { - return 1; - } else { - return a.compareTo(b); - } - } - } - - @Override - protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) { - if (isEmpty()) { - sb.append("{}"); - } else { - String[] keys = keySet().toArray(new String[size()]); - boolean singleton = keys.length == 1; - boolean shouldCompact = singleton && options.getCompactKeys(); - boolean outerBraces = options.getJson() || (!atRoot && !shouldCompact); - - int innerIndent; - if (outerBraces) { - innerIndent = indent + 1; - sb.append("{"); - - if (options.getFormatted()) - sb.append('\n'); - } else { - innerIndent = indent; - } - - int separatorCount = 0; - Arrays.sort(keys, new RenderComparator()); - for (String k : keys) { - com.gu.typesafe.config.impl.AbstractConfigValue v; - v = value.get(k); - - if (options.getOriginComments()) { - String[] lines = v.origin().description().split("\n"); - for (String l : lines) { - indent(sb, indent + 1, options); - sb.append('#'); - if (!l.isEmpty()) - sb.append(' '); - sb.append(l); - sb.append("\n"); - } - } - if (options.getComments()) { - for (String comment : v.origin().comments()) { - indent(sb, innerIndent, options); - sb.append("#"); - if (!comment.startsWith(" ")) - sb.append(' '); - sb.append(comment); - sb.append("\n"); - } - } - if (shouldCompact && !atRoot) { - sb.append("."); - } else { - indent(sb, innerIndent, options); - } - v.render(sb, innerIndent, false /* atRoot */, k, options); - - if (options.getFormatted()) { - if (options.getJson()) { - sb.append(","); - separatorCount = 2; - } else { - separatorCount = 1; - } - sb.append('\n'); - } else { - sb.append(","); - separatorCount = 1; - } - } - // chop last commas/newlines - sb.setLength(sb.length() - separatorCount); - - if (outerBraces) { - if (options.getFormatted()) { - sb.append('\n'); // put a newline back - if (outerBraces) - indent(sb, indent, options); - } - sb.append("}"); - } - } - if (atRoot && options.getFormatted()) - sb.append('\n'); - } - - @Override - public com.gu.typesafe.config.impl.AbstractConfigValue get(Object key) { - return value.get(key); - } - - private static boolean mapEquals(Map a, Map b) { - if (a == b) - return true; - - Set aKeys = a.keySet(); - Set bKeys = b.keySet(); - - if (!aKeys.equals(bKeys)) - return false; - - for (String key : aKeys) { - if (!a.get(key).equals(b.get(key))) - return false; - } - return true; - } - - private static int mapHash(Map m) { - // the keys have to be sorted, otherwise we could be equal - // to another map but have a different hashcode. - List keys = new ArrayList(); - keys.addAll(m.keySet()); - Collections.sort(keys); - - int valuesHash = 0; - for (String k : keys) { - valuesHash += m.get(k).hashCode(); - } - return 41 * (41 + keys.hashCode()) + valuesHash; - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof com.gu.typesafe.config.ConfigObject; - } - - @Override - public boolean equals(Object other) { - // note that "origin" is deliberately NOT part of equality. - // neither are other "extras" like ignoresFallbacks or resolve status. - if (other instanceof com.gu.typesafe.config.ConfigObject) { - // optimization to avoid unwrapped() for two ConfigObject, - // which is what AbstractConfigValue does. - return canEqual(other) && mapEquals(this, ((ConfigObject) other)); - } else { - return false; - } - } - - @Override - public int hashCode() { - // note that "origin" is deliberately NOT part of equality - // neither are other "extras" like ignoresFallbacks or resolve status. - return mapHash(this); - } - - @Override - public boolean containsKey(Object key) { - return value.containsKey(key); - } - - @Override - public Set keySet() { - return value.keySet(); - } - - @Override - public boolean containsValue(Object v) { - return value.containsValue(v); - } - - @Override - public Set> entrySet() { - // total bloat just to work around lack of type variance - - HashSet> entries = new HashSet>(); - for (Map.Entry e : value.entrySet()) { - entries.add(new AbstractMap.SimpleImmutableEntry( - e.getKey(), e - .getValue())); - } - return entries; - } - - @Override - public boolean isEmpty() { - return value.isEmpty(); - } - - @Override - public int size() { - return value.size(); - } - - @Override - public Collection values() { - return new HashSet(value.values()); - } - - final private static String EMPTY_NAME = "empty config"; - final private static SimpleConfigObject emptyInstance = empty(com.gu.typesafe.config.impl.SimpleConfigOrigin - .newSimple(EMPTY_NAME)); - - final static SimpleConfigObject empty() { - return emptyInstance; - } - - final static SimpleConfigObject empty(com.gu.typesafe.config.ConfigOrigin origin) { - if (origin == null) - return empty(); - else - return new SimpleConfigObject(origin, - Collections. emptyMap()); - } - - final static SimpleConfigObject emptyMissing(ConfigOrigin baseOrigin) { - return new SimpleConfigObject(com.gu.typesafe.config.impl.SimpleConfigOrigin.newSimple( - baseOrigin.description() + " (not found)"), - Collections. emptyMap()); - } - - // serialization all goes through SerializedConfigValue - private Object writeReplace() throws ObjectStreamException { - return new com.gu.typesafe.config.impl.SerializedConfigValue(this); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigOrigin.java b/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigOrigin.java deleted file mode 100644 index ea2b4d4a7a..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleConfigOrigin.java +++ /dev/null @@ -1,575 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.impl.SerializedConfigValue.SerializedField; - -// it would be cleaner to have a class hierarchy for various origin types, -// but was hoping this would be enough simpler to be a little messy. eh. -final class SimpleConfigOrigin implements com.gu.typesafe.config.ConfigOrigin { - - final private String description; - final private int lineNumber; - final private int endLineNumber; - final private OriginType originType; - final private String urlOrNull; - final private String resourceOrNull; - final private List commentsOrNull; - - protected SimpleConfigOrigin(String description, int lineNumber, int endLineNumber, OriginType originType, - String urlOrNull, String resourceOrNull, List commentsOrNull) { - if (description == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("description may not be null"); - this.description = description; - this.lineNumber = lineNumber; - this.endLineNumber = endLineNumber; - this.originType = originType; - this.urlOrNull = urlOrNull; - this.resourceOrNull = resourceOrNull; - this.commentsOrNull = commentsOrNull; - } - - static SimpleConfigOrigin newSimple(String description) { - return new SimpleConfigOrigin(description, -1, -1, OriginType.GENERIC, null, null, null); - } - - static SimpleConfigOrigin newFile(String filename) { - String url; - try { - url = (new File(filename)).toURI().toURL().toExternalForm(); - } catch (MalformedURLException e) { - url = null; - } - return new SimpleConfigOrigin(filename, -1, -1, OriginType.FILE, url, null, null); - } - - static SimpleConfigOrigin newURL(URL url) { - String u = url.toExternalForm(); - return new SimpleConfigOrigin(u, -1, -1, OriginType.URL, u, null, null); - } - - static SimpleConfigOrigin newResource(String resource, URL url) { - String desc; - if (url != null) - desc = resource + " @ " + url.toExternalForm(); - else - desc = resource; - return new SimpleConfigOrigin(desc, -1, -1, OriginType.RESOURCE, url != null ? url.toExternalForm() : null, - resource, null); - } - - static SimpleConfigOrigin newResource(String resource) { - return newResource(resource, null); - } - - @Override - public SimpleConfigOrigin withLineNumber(int lineNumber) { - if (lineNumber == this.lineNumber && lineNumber == this.endLineNumber) { - return this; - } else { - return new SimpleConfigOrigin(this.description, lineNumber, lineNumber, this.originType, this.urlOrNull, - this.resourceOrNull, this.commentsOrNull); - } - } - - SimpleConfigOrigin addURL(URL url) { - return new SimpleConfigOrigin(this.description, this.lineNumber, this.endLineNumber, this.originType, - url != null ? url.toExternalForm() : null, this.resourceOrNull, this.commentsOrNull); - } - - @Override - public SimpleConfigOrigin withComments(List comments) { - if (ConfigImplUtil.equalsHandlingNull(comments, this.commentsOrNull)) { - return this; - } else { - return new SimpleConfigOrigin(this.description, this.lineNumber, this.endLineNumber, this.originType, - this.urlOrNull, this.resourceOrNull, comments); - } - } - - SimpleConfigOrigin prependComments(List comments) { - if (ConfigImplUtil.equalsHandlingNull(comments, this.commentsOrNull) || comments == null) { - return this; - } else if (this.commentsOrNull == null) { - return withComments(comments); - } else { - List merged = new ArrayList(comments.size() + this.commentsOrNull.size()); - merged.addAll(comments); - merged.addAll(this.commentsOrNull); - return withComments(merged); - } - } - - SimpleConfigOrigin appendComments(List comments) { - if (ConfigImplUtil.equalsHandlingNull(comments, this.commentsOrNull) || comments == null) { - return this; - } else if (this.commentsOrNull == null) { - return withComments(comments); - } else { - List merged = new ArrayList(comments.size() + this.commentsOrNull.size()); - merged.addAll(this.commentsOrNull); - merged.addAll(comments); - return withComments(merged); - } - } - - @Override - public String description() { - if (lineNumber < 0) { - return description; - } else if (endLineNumber == lineNumber) { - return description + ": " + lineNumber; - } else { - return description + ": " + lineNumber + "-" + endLineNumber; - } - } - - @Override - public boolean equals(Object other) { - if (other instanceof SimpleConfigOrigin) { - SimpleConfigOrigin otherOrigin = (SimpleConfigOrigin) other; - - return this.description.equals(otherOrigin.description) && this.lineNumber == otherOrigin.lineNumber - && this.endLineNumber == otherOrigin.endLineNumber && this.originType == otherOrigin.originType - && ConfigImplUtil.equalsHandlingNull(this.urlOrNull, otherOrigin.urlOrNull) - && ConfigImplUtil.equalsHandlingNull(this.resourceOrNull, otherOrigin.resourceOrNull); - } else { - return false; - } - } - - @Override - public int hashCode() { - int h = 41 * (41 + description.hashCode()); - h = 41 * (h + lineNumber); - h = 41 * (h + endLineNumber); - h = 41 * (h + originType.hashCode()); - if (urlOrNull != null) - h = 41 * (h + urlOrNull.hashCode()); - if (resourceOrNull != null) - h = 41 * (h + resourceOrNull.hashCode()); - return h; - } - - @Override - public String toString() { - return "ConfigOrigin(" + description + ")"; - } - - @Override - public String filename() { - if (originType == OriginType.FILE) { - return description; - } else if (urlOrNull != null) { - URL url; - try { - url = new URL(urlOrNull); - } catch (MalformedURLException e) { - return null; - } - if (url.getProtocol().equals("file")) { - return url.getFile(); - } else { - return null; - } - } else { - return null; - } - } - - @Override - public URL url() { - if (urlOrNull == null) { - return null; - } else { - try { - return new URL(urlOrNull); - } catch (MalformedURLException e) { - return null; - } - } - } - - @Override - public String resource() { - return resourceOrNull; - } - - @Override - public int lineNumber() { - return lineNumber; - } - - @Override - public List comments() { - if (commentsOrNull != null) { - return Collections.unmodifiableList(commentsOrNull); - } else { - return Collections.emptyList(); - } - } - - static final String MERGE_OF_PREFIX = "merge of "; - - private static SimpleConfigOrigin mergeTwo(SimpleConfigOrigin a, SimpleConfigOrigin b) { - String mergedDesc; - int mergedStartLine; - int mergedEndLine; - List mergedComments; - - OriginType mergedType; - if (a.originType == b.originType) { - mergedType = a.originType; - } else { - mergedType = OriginType.GENERIC; - } - - // first use the "description" field which has no line numbers - // cluttering it. - String aDesc = a.description; - String bDesc = b.description; - if (aDesc.startsWith(MERGE_OF_PREFIX)) - aDesc = aDesc.substring(MERGE_OF_PREFIX.length()); - if (bDesc.startsWith(MERGE_OF_PREFIX)) - bDesc = bDesc.substring(MERGE_OF_PREFIX.length()); - - if (aDesc.equals(bDesc)) { - mergedDesc = aDesc; - - if (a.lineNumber < 0) - mergedStartLine = b.lineNumber; - else if (b.lineNumber < 0) - mergedStartLine = a.lineNumber; - else - mergedStartLine = Math.min(a.lineNumber, b.lineNumber); - - mergedEndLine = Math.max(a.endLineNumber, b.endLineNumber); - } else { - // this whole merge song-and-dance was intended to avoid this case - // whenever possible, but we've lost. Now we have to lose some - // structured information and cram into a string. - - // description() method includes line numbers, so use it instead - // of description field. - String aFull = a.description(); - String bFull = b.description(); - if (aFull.startsWith(MERGE_OF_PREFIX)) - aFull = aFull.substring(MERGE_OF_PREFIX.length()); - if (bFull.startsWith(MERGE_OF_PREFIX)) - bFull = bFull.substring(MERGE_OF_PREFIX.length()); - - mergedDesc = MERGE_OF_PREFIX + aFull + "," + bFull; - - mergedStartLine = -1; - mergedEndLine = -1; - } - - String mergedURL; - if (ConfigImplUtil.equalsHandlingNull(a.urlOrNull, b.urlOrNull)) { - mergedURL = a.urlOrNull; - } else { - mergedURL = null; - } - - String mergedResource; - if (ConfigImplUtil.equalsHandlingNull(a.resourceOrNull, b.resourceOrNull)) { - mergedResource = a.resourceOrNull; - } else { - mergedResource = null; - } - - if (ConfigImplUtil.equalsHandlingNull(a.commentsOrNull, b.commentsOrNull)) { - mergedComments = a.commentsOrNull; - } else { - mergedComments = new ArrayList(); - if (a.commentsOrNull != null) - mergedComments.addAll(a.commentsOrNull); - if (b.commentsOrNull != null) - mergedComments.addAll(b.commentsOrNull); - } - - return new SimpleConfigOrigin(mergedDesc, mergedStartLine, mergedEndLine, mergedType, mergedURL, - mergedResource, mergedComments); - } - - private static int similarity(SimpleConfigOrigin a, SimpleConfigOrigin b) { - int count = 0; - - if (a.originType == b.originType) - count += 1; - - if (a.description.equals(b.description)) { - count += 1; - - // only count these if the description field (which is the file - // or resource name) also matches. - if (a.lineNumber == b.lineNumber) - count += 1; - if (a.endLineNumber == b.endLineNumber) - count += 1; - if (ConfigImplUtil.equalsHandlingNull(a.urlOrNull, b.urlOrNull)) - count += 1; - if (ConfigImplUtil.equalsHandlingNull(a.resourceOrNull, b.resourceOrNull)) - count += 1; - } - - return count; - } - - // this picks the best pair to merge, because the pair has the most in - // common. we want to merge two lines in the same file rather than something - // else with one of the lines; because two lines in the same file can be - // better consolidated. - private static SimpleConfigOrigin mergeThree(SimpleConfigOrigin a, SimpleConfigOrigin b, SimpleConfigOrigin c) { - if (similarity(a, b) >= similarity(b, c)) { - return mergeTwo(mergeTwo(a, b), c); - } else { - return mergeTwo(a, mergeTwo(b, c)); - } - } - - static com.gu.typesafe.config.ConfigOrigin mergeOrigins(com.gu.typesafe.config.ConfigOrigin a, com.gu.typesafe.config.ConfigOrigin b) { - return mergeTwo((SimpleConfigOrigin) a, (SimpleConfigOrigin) b); - } - - static com.gu.typesafe.config.ConfigOrigin mergeOrigins(List stack) { - List origins = new ArrayList(stack.size()); - for (com.gu.typesafe.config.impl.AbstractConfigValue v : stack) { - origins.add(v.origin()); - } - return mergeOrigins(origins); - } - - static com.gu.typesafe.config.ConfigOrigin mergeOrigins(Collection stack) { - if (stack.isEmpty()) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("can't merge empty list of origins"); - } else if (stack.size() == 1) { - return stack.iterator().next(); - } else if (stack.size() == 2) { - Iterator i = stack.iterator(); - return mergeTwo((SimpleConfigOrigin) i.next(), (SimpleConfigOrigin) i.next()); - } else { - List remaining = new ArrayList(stack.size()); - for (ConfigOrigin o : stack) { - remaining.add((SimpleConfigOrigin) o); - } - while (remaining.size() > 2) { - SimpleConfigOrigin c = remaining.get(remaining.size() - 1); - remaining.remove(remaining.size() - 1); - SimpleConfigOrigin b = remaining.get(remaining.size() - 1); - remaining.remove(remaining.size() - 1); - SimpleConfigOrigin a = remaining.get(remaining.size() - 1); - remaining.remove(remaining.size() - 1); - - SimpleConfigOrigin merged = mergeThree(a, b, c); - - remaining.add(merged); - } - - // should be down to either 1 or 2 - return mergeOrigins(remaining); - } - } - - Map toFields() { - Map m = new EnumMap(SerializedField.class); - - m.put(SerializedField.ORIGIN_DESCRIPTION, description); - - if (lineNumber >= 0) - m.put(SerializedField.ORIGIN_LINE_NUMBER, lineNumber); - if (endLineNumber >= 0) - m.put(SerializedField.ORIGIN_END_LINE_NUMBER, endLineNumber); - - m.put(SerializedField.ORIGIN_TYPE, originType.ordinal()); - - if (urlOrNull != null) - m.put(SerializedField.ORIGIN_URL, urlOrNull); - if (resourceOrNull != null) - m.put(SerializedField.ORIGIN_RESOURCE, resourceOrNull); - if (commentsOrNull != null) - m.put(SerializedField.ORIGIN_COMMENTS, commentsOrNull); - - return m; - } - - Map toFieldsDelta(SimpleConfigOrigin baseOrigin) { - Map baseFields; - if (baseOrigin != null) - baseFields = baseOrigin.toFields(); - else - baseFields = Collections. emptyMap(); - return fieldsDelta(baseFields, toFields()); - } - - // Here we're trying to avoid serializing the same info over and over - // in the common case that child objects have the same origin fields - // as their parent objects. e.g. we don't need to store the source - // filename with every single value. - static Map fieldsDelta(Map base, - Map child) { - Map m = new EnumMap(child); - - for (Map.Entry baseEntry : base.entrySet()) { - SerializedField f = baseEntry.getKey(); - if (m.containsKey(f) && ConfigImplUtil.equalsHandlingNull(baseEntry.getValue(), m.get(f))) { - // if field is unchanged, just remove it so we inherit - m.remove(f); - } else if (!m.containsKey(f)) { - // if field has been removed, we have to add a deletion entry - switch (f) { - case ORIGIN_DESCRIPTION: - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("origin missing description field? " + child); - case ORIGIN_LINE_NUMBER: - m.put(SerializedField.ORIGIN_LINE_NUMBER, -1); - break; - case ORIGIN_END_LINE_NUMBER: - m.put(SerializedField.ORIGIN_END_LINE_NUMBER, -1); - break; - case ORIGIN_TYPE: - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("should always be an ORIGIN_TYPE field"); - case ORIGIN_URL: - m.put(SerializedField.ORIGIN_NULL_URL, ""); - break; - case ORIGIN_RESOURCE: - m.put(SerializedField.ORIGIN_NULL_RESOURCE, ""); - break; - case ORIGIN_COMMENTS: - m.put(SerializedField.ORIGIN_NULL_COMMENTS, ""); - break; - case ORIGIN_NULL_URL: // FALL THRU - case ORIGIN_NULL_RESOURCE: // FALL THRU - case ORIGIN_NULL_COMMENTS: - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("computing delta, base object should not contain " + f + " " - + base); - case END_MARKER: - case ROOT_VALUE: - case ROOT_WAS_CONFIG: - case UNKNOWN: - case VALUE_DATA: - case VALUE_ORIGIN: - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("should not appear here: " + f); - } - } else { - // field is in base and child, but differs, so leave it - } - } - - return m; - } - - static SimpleConfigOrigin fromFields(Map m) throws IOException { - // we represent a null origin as one with no fields at all - if (m.isEmpty()) - return null; - - String description = (String) m.get(SerializedField.ORIGIN_DESCRIPTION); - Integer lineNumber = (Integer) m.get(SerializedField.ORIGIN_LINE_NUMBER); - Integer endLineNumber = (Integer) m.get(SerializedField.ORIGIN_END_LINE_NUMBER); - Number originTypeOrdinal = (Number) m.get(SerializedField.ORIGIN_TYPE); - if (originTypeOrdinal == null) - throw new IOException("Missing ORIGIN_TYPE field"); - OriginType originType = OriginType.values()[originTypeOrdinal.byteValue()]; - String urlOrNull = (String) m.get(SerializedField.ORIGIN_URL); - String resourceOrNull = (String) m.get(SerializedField.ORIGIN_RESOURCE); - @SuppressWarnings("unchecked") - List commentsOrNull = (List) m.get(SerializedField.ORIGIN_COMMENTS); - // Older versions did not have a resource field, they stuffed it into - // the description. - if (originType == OriginType.RESOURCE && resourceOrNull == null) { - resourceOrNull = description; - } - return new SimpleConfigOrigin(description, lineNumber != null ? lineNumber : -1, - endLineNumber != null ? endLineNumber : -1, originType, urlOrNull, resourceOrNull, commentsOrNull); - } - - static Map applyFieldsDelta(Map base, - Map delta) throws IOException { - - Map m = new EnumMap(delta); - - for (Map.Entry baseEntry : base.entrySet()) { - SerializedField f = baseEntry.getKey(); - if (delta.containsKey(f)) { - // delta overrides when keys are in both - // "m" should already contain the right thing - } else { - // base has the key and delta does not. - // we inherit from base unless a "NULL" key blocks. - switch (f) { - case ORIGIN_DESCRIPTION: - m.put(f, base.get(f)); - break; - case ORIGIN_URL: - if (delta.containsKey(SerializedField.ORIGIN_NULL_URL)) { - m.remove(SerializedField.ORIGIN_NULL_URL); - } else { - m.put(f, base.get(f)); - } - break; - case ORIGIN_RESOURCE: - if (delta.containsKey(SerializedField.ORIGIN_NULL_RESOURCE)) { - m.remove(SerializedField.ORIGIN_NULL_RESOURCE); - } else { - m.put(f, base.get(f)); - } - break; - case ORIGIN_COMMENTS: - if (delta.containsKey(SerializedField.ORIGIN_NULL_COMMENTS)) { - m.remove(SerializedField.ORIGIN_NULL_COMMENTS); - } else { - m.put(f, base.get(f)); - } - break; - case ORIGIN_NULL_URL: // FALL THRU - case ORIGIN_NULL_RESOURCE: // FALL THRU - case ORIGIN_NULL_COMMENTS: // FALL THRU - // base objects shouldn't contain these, should just - // lack the field. these are only in deltas. - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("applying fields, base object should not contain " + f + " " - + base); - case ORIGIN_END_LINE_NUMBER: // FALL THRU - case ORIGIN_LINE_NUMBER: // FALL THRU - case ORIGIN_TYPE: - m.put(f, base.get(f)); - break; - - case END_MARKER: - case ROOT_VALUE: - case ROOT_WAS_CONFIG: - case UNKNOWN: - case VALUE_DATA: - case VALUE_ORIGIN: - throw new ConfigException.BugOrBroken("should not appear here: " + f); - } - } - } - return m; - } - - static SimpleConfigOrigin fromBase(SimpleConfigOrigin baseOrigin, Map delta) - throws IOException { - Map baseFields; - if (baseOrigin != null) - baseFields = baseOrigin.toFields(); - else - baseFields = Collections. emptyMap(); - Map fields = applyFieldsDelta(baseFields, delta); - return fromFields(fields); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleIncludeContext.java b/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleIncludeContext.java deleted file mode 100644 index 646f6a0d16..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleIncludeContext.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigIncludeContext; -import com.gu.typesafe.config.ConfigParseOptions; -import com.gu.typesafe.config.ConfigParseable; - -class SimpleIncludeContext implements com.gu.typesafe.config.ConfigIncludeContext { - - private final com.gu.typesafe.config.impl.Parseable parseable; - private final com.gu.typesafe.config.ConfigParseOptions options; - - SimpleIncludeContext(com.gu.typesafe.config.impl.Parseable parseable) { - this.parseable = parseable; - this.options = SimpleIncluder.clearForInclude(parseable.options()); - } - - private SimpleIncludeContext(com.gu.typesafe.config.impl.Parseable parseable, com.gu.typesafe.config.ConfigParseOptions options) { - this.parseable = parseable; - this.options = options; - } - - SimpleIncludeContext withParseable(Parseable parseable) { - if (parseable == this.parseable) - return this; - else - return new SimpleIncludeContext(parseable); - } - - @Override - public ConfigParseable relativeTo(String filename) { - if (ConfigImpl.traceLoadsEnabled()) - ConfigImpl.trace("Looking for '" + filename + "' relative to " + parseable); - if (parseable != null) - return parseable.relativeTo(filename); - else - return null; - } - - @Override - public com.gu.typesafe.config.ConfigParseOptions parseOptions() { - return options; - } - - @Override - public ConfigIncludeContext setParseOptions(ConfigParseOptions options) { - return new SimpleIncludeContext(parseable, options.setSyntax(null).setOriginDescription(null)); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleIncluder.java b/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleIncluder.java deleted file mode 100644 index 15621b02bc..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/SimpleIncluder.java +++ /dev/null @@ -1,293 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -import com.gu.typesafe.config.*; -import com.gu.typesafe.config.ConfigIncluderClasspath; - -class SimpleIncluder implements com.gu.typesafe.config.impl.FullIncluder { - - private com.gu.typesafe.config.ConfigIncluder fallback; - - SimpleIncluder(com.gu.typesafe.config.ConfigIncluder fallback) { - this.fallback = fallback; - } - - // ConfigIncludeContext does this for us on its options - static com.gu.typesafe.config.ConfigParseOptions clearForInclude(com.gu.typesafe.config.ConfigParseOptions options) { - // the class loader and includer are inherited, but not this other - // stuff. - return options.setSyntax(null).setOriginDescription(null).setAllowMissing(true); - } - - // this is the heuristic includer - @Override - public com.gu.typesafe.config.ConfigObject include(final com.gu.typesafe.config.ConfigIncludeContext context, String name) { - com.gu.typesafe.config.ConfigObject obj = includeWithoutFallback(context, name); - - // now use the fallback includer if any and merge - // its result. - if (fallback != null) { - return obj.withFallback(fallback.include(context, name)); - } else { - return obj; - } - } - - // the heuristic includer in static form - static com.gu.typesafe.config.ConfigObject includeWithoutFallback(final com.gu.typesafe.config.ConfigIncludeContext context, String name) { - // the heuristic is valid URL then URL, else relative to including file; - // relativeTo in a file falls back to classpath inside relativeTo(). - - URL url; - try { - url = new URL(name); - } catch (MalformedURLException e) { - url = null; - } - - if (url != null) { - return includeURLWithoutFallback(context, url); - } else { - NameSource source = new RelativeNameSource(context); - return fromBasename(source, name, context.parseOptions()); - } - } - - @Override - public com.gu.typesafe.config.ConfigObject includeURL(com.gu.typesafe.config.ConfigIncludeContext context, URL url) { - com.gu.typesafe.config.ConfigObject obj = includeURLWithoutFallback(context, url); - - // now use the fallback includer if any and merge - // its result. - if (fallback != null && fallback instanceof com.gu.typesafe.config.ConfigIncluderURL) { - return obj.withFallback(((com.gu.typesafe.config.ConfigIncluderURL) fallback).includeURL(context, url)); - } else { - return obj; - } - } - - static com.gu.typesafe.config.ConfigObject includeURLWithoutFallback(final com.gu.typesafe.config.ConfigIncludeContext context, URL url) { - return com.gu.typesafe.config.ConfigFactory.parseURL(url, context.parseOptions()).root(); - } - - @Override - public com.gu.typesafe.config.ConfigObject includeFile(com.gu.typesafe.config.ConfigIncludeContext context, File file) { - com.gu.typesafe.config.ConfigObject obj = includeFileWithoutFallback(context, file); - - // now use the fallback includer if any and merge - // its result. - if (fallback != null && fallback instanceof com.gu.typesafe.config.ConfigIncluderFile) { - return obj.withFallback(((com.gu.typesafe.config.ConfigIncluderFile) fallback).includeFile(context, file)); - } else { - return obj; - } - } - - static com.gu.typesafe.config.ConfigObject includeFileWithoutFallback(final com.gu.typesafe.config.ConfigIncludeContext context, File file) { - return com.gu.typesafe.config.ConfigFactory.parseFileAnySyntax(file, context.parseOptions()).root(); - } - - @Override - public com.gu.typesafe.config.ConfigObject includeResources(com.gu.typesafe.config.ConfigIncludeContext context, String resource) { - com.gu.typesafe.config.ConfigObject obj = includeResourceWithoutFallback(context, resource); - - // now use the fallback includer if any and merge - // its result. - if (fallback != null && fallback instanceof ConfigIncluderClasspath) { - return obj.withFallback(((ConfigIncluderClasspath) fallback).includeResources(context, - resource)); - } else { - return obj; - } - } - - static com.gu.typesafe.config.ConfigObject includeResourceWithoutFallback(final com.gu.typesafe.config.ConfigIncludeContext context, - String resource) { - return ConfigFactory.parseResourcesAnySyntax(resource, context.parseOptions()).root(); - } - - @Override - public com.gu.typesafe.config.ConfigIncluder withFallback(com.gu.typesafe.config.ConfigIncluder fallback) { - if (this == fallback) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("trying to create includer cycle"); - } else if (this.fallback == fallback) { - return this; - } else if (this.fallback != null) { - return new SimpleIncluder(this.fallback.withFallback(fallback)); - } else { - return new SimpleIncluder(fallback); - } - } - - interface NameSource { - com.gu.typesafe.config.ConfigParseable nameToParseable(String name, com.gu.typesafe.config.ConfigParseOptions parseOptions); - } - - static private class RelativeNameSource implements NameSource { - final private com.gu.typesafe.config.ConfigIncludeContext context; - - RelativeNameSource(com.gu.typesafe.config.ConfigIncludeContext context) { - this.context = context; - } - - @Override - public com.gu.typesafe.config.ConfigParseable nameToParseable(String name, com.gu.typesafe.config.ConfigParseOptions options) { - com.gu.typesafe.config.ConfigParseable p = context.relativeTo(name); - if (p == null) { - // avoid returning null - return Parseable - .newNotFound(name, "include was not found: '" + name + "'", options); - } else { - return p; - } - } - }; - - // this function is a little tricky because there are three places we're - // trying to use it; for 'include "basename"' in a .conf file, for - // loading app.{conf,json,properties} from classpath, and for - // loading app.{conf,json,properties} from the filesystem. - static com.gu.typesafe.config.ConfigObject fromBasename(NameSource source, String name, ConfigParseOptions options) { - com.gu.typesafe.config.ConfigObject obj; - if (name.endsWith(".conf") || name.endsWith(".json") || name.endsWith(".properties")) { - com.gu.typesafe.config.ConfigParseable p = source.nameToParseable(name, options); - - obj = p.parse(p.options().setAllowMissing(options.getAllowMissing())); - } else { - com.gu.typesafe.config.ConfigParseable confHandle = source.nameToParseable(name + ".conf", options); - com.gu.typesafe.config.ConfigParseable jsonHandle = source.nameToParseable(name + ".json", options); - ConfigParseable propsHandle = source.nameToParseable(name + ".properties", options); - boolean gotSomething = false; - List fails = new ArrayList(); - - com.gu.typesafe.config.ConfigSyntax syntax = options.getSyntax(); - - obj = com.gu.typesafe.config.impl.SimpleConfigObject.empty(com.gu.typesafe.config.impl.SimpleConfigOrigin.newSimple(name)); - if (syntax == null || syntax == com.gu.typesafe.config.ConfigSyntax.CONF) { - try { - obj = confHandle.parse(confHandle.options().setAllowMissing(false) - .setSyntax(com.gu.typesafe.config.ConfigSyntax.CONF)); - gotSomething = true; - } catch (com.gu.typesafe.config.ConfigException.IO e) { - fails.add(e); - } - } - - if (syntax == null || syntax == com.gu.typesafe.config.ConfigSyntax.JSON) { - try { - com.gu.typesafe.config.ConfigObject parsed = jsonHandle.parse(jsonHandle.options() - .setAllowMissing(false).setSyntax(com.gu.typesafe.config.ConfigSyntax.JSON)); - obj = obj.withFallback(parsed); - gotSomething = true; - } catch (com.gu.typesafe.config.ConfigException.IO e) { - fails.add(e); - } - } - - if (syntax == null || syntax == com.gu.typesafe.config.ConfigSyntax.PROPERTIES) { - try { - com.gu.typesafe.config.ConfigObject parsed = propsHandle.parse(propsHandle.options() - .setAllowMissing(false).setSyntax(ConfigSyntax.PROPERTIES)); - obj = obj.withFallback(parsed); - gotSomething = true; - } catch (com.gu.typesafe.config.ConfigException.IO e) { - fails.add(e); - } - } - - if (!options.getAllowMissing() && !gotSomething) { - if (com.gu.typesafe.config.impl.ConfigImpl.traceLoadsEnabled()) { - // the individual exceptions should have been logged already - // with tracing enabled - com.gu.typesafe.config.impl.ConfigImpl.trace("Did not find '" + name - + "' with any extension (.conf, .json, .properties); " - + "exceptions should have been logged above."); - } - - if (fails.isEmpty()) { - // this should not happen - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "should not be reached: nothing found but no exceptions thrown"); - } else { - StringBuilder sb = new StringBuilder(); - for (Throwable t : fails) { - sb.append(t.getMessage()); - sb.append(", "); - } - sb.setLength(sb.length() - 2); - throw new ConfigException.IO(com.gu.typesafe.config.impl.SimpleConfigOrigin.newSimple(name), sb.toString(), - fails.get(0)); - } - } else if (!gotSomething) { - if (com.gu.typesafe.config.impl.ConfigImpl.traceLoadsEnabled()) { - ConfigImpl.trace("Did not find '" + name - + "' with any extension (.conf, .json, .properties); but '" + name - + "' is allowed to be missing. Exceptions from load attempts should have been logged above."); - } - } - } - - return obj; - } - - // the Proxy is a proxy for an application-provided includer that uses our - // default implementations when the application-provided includer doesn't - // have an implementation. - static private class Proxy implements com.gu.typesafe.config.impl.FullIncluder { - final com.gu.typesafe.config.ConfigIncluder delegate; - - Proxy(com.gu.typesafe.config.ConfigIncluder delegate) { - this.delegate = delegate; - } - - @Override - public com.gu.typesafe.config.ConfigIncluder withFallback(com.gu.typesafe.config.ConfigIncluder fallback) { - // we never fall back - return this; - } - - @Override - public com.gu.typesafe.config.ConfigObject include(com.gu.typesafe.config.ConfigIncludeContext context, String what) { - return delegate.include(context, what); - } - - @Override - public com.gu.typesafe.config.ConfigObject includeResources(com.gu.typesafe.config.ConfigIncludeContext context, String what) { - if (delegate instanceof ConfigIncluderClasspath) - return ((ConfigIncluderClasspath) delegate).includeResources(context, what); - else - return includeResourceWithoutFallback(context, what); - } - - @Override - public com.gu.typesafe.config.ConfigObject includeURL(com.gu.typesafe.config.ConfigIncludeContext context, URL what) { - if (delegate instanceof com.gu.typesafe.config.ConfigIncluderURL) - return ((ConfigIncluderURL) delegate).includeURL(context, what); - else - return includeURLWithoutFallback(context, what); - } - - @Override - public ConfigObject includeFile(ConfigIncludeContext context, File what) { - if (delegate instanceof com.gu.typesafe.config.ConfigIncluderFile) - return ((ConfigIncluderFile) delegate).includeFile(context, what); - else - return includeFileWithoutFallback(context, what); - } - } - - static com.gu.typesafe.config.impl.FullIncluder makeFull(ConfigIncluder includer) { - if (includer instanceof com.gu.typesafe.config.impl.FullIncluder) - return (com.gu.typesafe.config.impl.FullIncluder) includer; - else - return new Proxy(includer); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/SubstitutionExpression.java b/scripts/src/main/java/com/gu/typesafe/config/impl/SubstitutionExpression.java deleted file mode 100644 index 873ecf81b2..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/SubstitutionExpression.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.gu.typesafe.config.impl; - -final class SubstitutionExpression { - - final private Path path; - final private boolean optional; - - SubstitutionExpression(Path path, boolean optional) { - this.path = path; - this.optional = optional; - } - - Path path() { - return path; - } - - boolean optional() { - return optional; - } - - SubstitutionExpression changePath(Path newPath) { - if (newPath == path) - return this; - else - return new SubstitutionExpression(newPath, optional); - } - - @Override - public String toString() { - return "${" + (optional ? "?" : "") + path.render() + "}"; - } - - @Override - public boolean equals(Object other) { - if (other instanceof SubstitutionExpression) { - SubstitutionExpression otherExp = (SubstitutionExpression) other; - return otherExp.path.equals(this.path) && otherExp.optional == this.optional; - } else { - return false; - } - } - - @Override - public int hashCode() { - int h = 41 * (41 + path.hashCode()); - h = 41 * (h + (optional ? 1 : 0)); - return h; - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/Token.java b/scripts/src/main/java/com/gu/typesafe/config/impl/Token.java deleted file mode 100644 index 071356d109..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/Token.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; - -class Token { - final private com.gu.typesafe.config.impl.TokenType tokenType; - final private String debugString; - final private com.gu.typesafe.config.ConfigOrigin origin; - final private String tokenText; - - Token(com.gu.typesafe.config.impl.TokenType tokenType, com.gu.typesafe.config.ConfigOrigin origin) { - this(tokenType, origin, null); - } - - Token(com.gu.typesafe.config.impl.TokenType tokenType, com.gu.typesafe.config.ConfigOrigin origin, String tokenText) { - this(tokenType, origin, tokenText, null); - } - - Token(com.gu.typesafe.config.impl.TokenType tokenType, com.gu.typesafe.config.ConfigOrigin origin, String tokenText, String debugString) { - this.tokenType = tokenType; - this.origin = origin; - this.debugString = debugString; - this.tokenText = tokenText; - } - - // this is used for singleton tokens like COMMA or OPEN_CURLY - static Token newWithoutOrigin(com.gu.typesafe.config.impl.TokenType tokenType, String debugString, String tokenText) { - return new Token(tokenType, null, tokenText, debugString); - } - - final com.gu.typesafe.config.impl.TokenType tokenType() { - return tokenType; - } - - public String tokenText() { return tokenText; } - - // this is final because we don't always use the origin() accessor, - // and we don't because it throws if origin is null - final ConfigOrigin origin() { - // code is only supposed to call origin() on token types that are - // expected to have an origin. - if (origin == null) - throw new ConfigException.BugOrBroken( - "tried to get origin from token that doesn't have one: " + this); - return origin; - } - - final int lineNumber() { - if (origin != null) - return origin.lineNumber(); - else - return -1; - } - - @Override - public String toString() { - if (debugString != null) - return debugString; - else - return tokenType.name(); - } - - protected boolean canEqual(Object other) { - return other instanceof Token; - } - - @Override - public boolean equals(Object other) { - if (other instanceof Token) { - // origin is deliberately left out - return canEqual(other) - && this.tokenType == ((Token) other).tokenType; - } else { - return false; - } - } - - @Override - public int hashCode() { - // origin is deliberately left out - return tokenType.hashCode(); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/TokenType.java b/scripts/src/main/java/com/gu/typesafe/config/impl/TokenType.java deleted file mode 100644 index a8261fb4ee..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/TokenType.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -enum TokenType { - START, - END, - COMMA, - EQUALS, - COLON, - OPEN_CURLY, - CLOSE_CURLY, - OPEN_SQUARE, - CLOSE_SQUARE, - VALUE, - NEWLINE, - UNQUOTED_TEXT, - IGNORED_WHITESPACE, - SUBSTITUTION, - PROBLEM, - COMMENT, - PLUS_EQUALS; -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/Tokenizer.java b/scripts/src/main/java/com/gu/typesafe/config/impl/Tokenizer.java deleted file mode 100644 index 1dea55ebd6..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/Tokenizer.java +++ /dev/null @@ -1,695 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.io.IOException; -import java.io.Reader; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigSyntax; - -final class Tokenizer { - // this exception should not leave this file - private static class ProblemException extends Exception { - private static final long serialVersionUID = 1L; - - final private com.gu.typesafe.config.impl.Token problem; - - ProblemException(com.gu.typesafe.config.impl.Token problem) { - this.problem = problem; - } - - com.gu.typesafe.config.impl.Token problem() { - return problem; - } - } - - private static String asString(int codepoint) { - if (codepoint == '\n') - return "newline"; - else if (codepoint == '\t') - return "tab"; - else if (codepoint == -1) - return "end of file"; - else if (com.gu.typesafe.config.impl.ConfigImplUtil.isC0Control(codepoint)) - return String.format("control character 0x%x", codepoint); - else - return String.format("%c", codepoint); - } - - /** - * Tokenizes a Reader. Does not close the reader; you have to arrange to do - * that after you're done with the returned iterator. - */ - static Iterator tokenize(com.gu.typesafe.config.ConfigOrigin origin, Reader input, com.gu.typesafe.config.ConfigSyntax flavor) { - return new TokenIterator(origin, input, flavor != ConfigSyntax.JSON); - } - - static String render(Iterator tokens) { - StringBuilder renderedText = new StringBuilder(); - while (tokens.hasNext()) { - renderedText.append(tokens.next().tokenText()); - } - return renderedText.toString(); - } - - private static class TokenIterator implements Iterator { - - private static class WhitespaceSaver { - // has to be saved inside value concatenations - private StringBuilder whitespace; - // may need to value-concat with next value - private boolean lastTokenWasSimpleValue; - - WhitespaceSaver() { - whitespace = new StringBuilder(); - lastTokenWasSimpleValue = false; - } - - void add(int c) { - whitespace.appendCodePoint(c); - } - - com.gu.typesafe.config.impl.Token check(com.gu.typesafe.config.impl.Token t, com.gu.typesafe.config.ConfigOrigin baseOrigin, int lineNumber) { - if (isSimpleValue(t)) { - return nextIsASimpleValue(baseOrigin, lineNumber); - } else { - return nextIsNotASimpleValue(baseOrigin, lineNumber); - } - } - - // called if the next token is not a simple value; - // discards any whitespace we were saving between - // simple values. - private com.gu.typesafe.config.impl.Token nextIsNotASimpleValue(com.gu.typesafe.config.ConfigOrigin baseOrigin, int lineNumber) { - lastTokenWasSimpleValue = false; - return createWhitespaceTokenFromSaver(baseOrigin, lineNumber); - } - - // called if the next token IS a simple value, - // so creates a whitespace token if the previous - // token also was. - private com.gu.typesafe.config.impl.Token nextIsASimpleValue(com.gu.typesafe.config.ConfigOrigin baseOrigin, - int lineNumber) { - com.gu.typesafe.config.impl.Token t = createWhitespaceTokenFromSaver(baseOrigin, lineNumber); - if (!lastTokenWasSimpleValue) { - lastTokenWasSimpleValue = true; - } - return t; - } - - private com.gu.typesafe.config.impl.Token createWhitespaceTokenFromSaver(com.gu.typesafe.config.ConfigOrigin baseOrigin, - int lineNumber) { - if (whitespace.length() > 0) { - com.gu.typesafe.config.impl.Token t; - if (lastTokenWasSimpleValue) { - t = com.gu.typesafe.config.impl.Tokens.newUnquotedText( - lineOrigin(baseOrigin, lineNumber), - whitespace.toString()); - } else { - t = com.gu.typesafe.config.impl.Tokens.newIgnoredWhitespace(lineOrigin(baseOrigin, lineNumber), - whitespace.toString()); - } - whitespace.setLength(0); // reset - return t; - } - return null; - } - } - - final private com.gu.typesafe.config.impl.SimpleConfigOrigin origin; - final private Reader input; - final private LinkedList buffer; - private int lineNumber; - private com.gu.typesafe.config.ConfigOrigin lineOrigin; - final private Queue tokens; - final private WhitespaceSaver whitespaceSaver; - final private boolean allowComments; - - TokenIterator(com.gu.typesafe.config.ConfigOrigin origin, Reader input, boolean allowComments) { - this.origin = (com.gu.typesafe.config.impl.SimpleConfigOrigin) origin; - this.input = input; - this.allowComments = allowComments; - this.buffer = new LinkedList(); - lineNumber = 1; - lineOrigin = this.origin.withLineNumber(lineNumber); - tokens = new LinkedList(); - tokens.add(com.gu.typesafe.config.impl.Tokens.START); - whitespaceSaver = new WhitespaceSaver(); - } - - - // this should ONLY be called from nextCharSkippingComments - // or when inside a quoted string, or when parsing a sequence - // like ${ or +=, everything else should use - // nextCharSkippingComments(). - private int nextCharRaw() { - if (buffer.isEmpty()) { - try { - return input.read(); - } catch (IOException e) { - throw new com.gu.typesafe.config.ConfigException.IO(origin, "read error: " - + e.getMessage(), e); - } - } else { - int c = buffer.pop(); - return c; - } - } - - private void putBack(int c) { - if (buffer.size() > 2) { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "bug: putBack() three times, undesirable look-ahead"); - } - buffer.push(c); - } - - static boolean isWhitespace(int c) { - return com.gu.typesafe.config.impl.ConfigImplUtil.isWhitespace(c); - } - - static boolean isWhitespaceNotNewline(int c) { - return c != '\n' && com.gu.typesafe.config.impl.ConfigImplUtil.isWhitespace(c); - } - - private boolean startOfComment(int c) { - if (c == -1) { - return false; - } else { - if (allowComments) { - if (c == '#') { - return true; - } else if (c == '/') { - int maybeSecondSlash = nextCharRaw(); - // we want to predictably NOT consume any chars - putBack(maybeSecondSlash); - if (maybeSecondSlash == '/') { - return true; - } else { - return false; - } - } else { - return false; - } - } else { - return false; - } - } - } - - // get next char, skipping non-newline whitespace - private int nextCharAfterWhitespace(WhitespaceSaver saver) { - for (;;) { - int c = nextCharRaw(); - - if (c == -1) { - return -1; - } else { - if (isWhitespaceNotNewline(c)) { - saver.add(c); - continue; - } else { - return c; - } - } - } - } - - private ProblemException problem(String message) { - return problem("", message, null); - } - - private ProblemException problem(String what, String message) { - return problem(what, message, null); - } - - private ProblemException problem(String what, String message, boolean suggestQuotes) { - return problem(what, message, suggestQuotes, null); - } - - private ProblemException problem(String what, String message, Throwable cause) { - return problem(lineOrigin, what, message, cause); - } - - private ProblemException problem(String what, String message, boolean suggestQuotes, - Throwable cause) { - return problem(lineOrigin, what, message, suggestQuotes, cause); - } - - private static ProblemException problem(com.gu.typesafe.config.ConfigOrigin origin, String what, - String message, - Throwable cause) { - return problem(origin, what, message, false, cause); - } - - private static ProblemException problem(com.gu.typesafe.config.ConfigOrigin origin, String what, String message, - boolean suggestQuotes, Throwable cause) { - if (what == null || message == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "internal error, creating bad ProblemException"); - return new ProblemException(com.gu.typesafe.config.impl.Tokens.newProblem(origin, what, message, suggestQuotes, - cause)); - } - - private static ProblemException problem(com.gu.typesafe.config.ConfigOrigin origin, String message) { - return problem(origin, "", message, null); - } - - private static com.gu.typesafe.config.ConfigOrigin lineOrigin(com.gu.typesafe.config.ConfigOrigin baseOrigin, - int lineNumber) { - return ((com.gu.typesafe.config.impl.SimpleConfigOrigin) baseOrigin).withLineNumber(lineNumber); - } - - // ONE char has always been consumed, either the # or the first /, but - // not both slashes - private com.gu.typesafe.config.impl.Token pullComment(int firstChar) { - boolean doubleSlash = false; - if (firstChar == '/') { - int discard = nextCharRaw(); - if (discard != '/') - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("called pullComment but // not seen"); - doubleSlash = true; - } - - StringBuilder sb = new StringBuilder(); - for (;;) { - int c = nextCharRaw(); - if (c == -1 || c == '\n') { - putBack(c); - if (doubleSlash) - return com.gu.typesafe.config.impl.Tokens.newCommentDoubleSlash(lineOrigin, sb.toString()); - else - return com.gu.typesafe.config.impl.Tokens.newCommentHash(lineOrigin, sb.toString()); - } else { - sb.appendCodePoint(c); - } - } - } - - // chars JSON allows a number to start with - static final String firstNumberChars = "0123456789-"; - // chars JSON allows to be part of a number - static final String numberChars = "0123456789eE+-."; - // chars that stop an unquoted string - static final String notInUnquotedText = "$\"{}[]:=,+#`^?!@*&\\"; - - // The rules here are intended to maximize convenience while - // avoiding confusion with real valid JSON. Basically anything - // that parses as JSON is treated the JSON way and otherwise - // we assume it's a string and let the parser sort it out. - private com.gu.typesafe.config.impl.Token pullUnquotedText() { - com.gu.typesafe.config.ConfigOrigin origin = lineOrigin; - StringBuilder sb = new StringBuilder(); - int c = nextCharRaw(); - while (true) { - if (c == -1) { - break; - } else if (notInUnquotedText.indexOf(c) >= 0) { - break; - } else if (isWhitespace(c)) { - break; - } else if (startOfComment(c)) { - break; - } else { - sb.appendCodePoint(c); - } - - // we parse true/false/null tokens as such no matter - // what is after them, as long as they are at the - // start of the unquoted token. - if (sb.length() == 4) { - String s = sb.toString(); - if (s.equals("true")) - return com.gu.typesafe.config.impl.Tokens.newBoolean(origin, true); - else if (s.equals("null")) - return com.gu.typesafe.config.impl.Tokens.newNull(origin); - } else if (sb.length() == 5) { - String s = sb.toString(); - if (s.equals("false")) - return com.gu.typesafe.config.impl.Tokens.newBoolean(origin, false); - } - - c = nextCharRaw(); - } - - // put back the char that ended the unquoted text - putBack(c); - - String s = sb.toString(); - return com.gu.typesafe.config.impl.Tokens.newUnquotedText(origin, s); - } - - private com.gu.typesafe.config.impl.Token pullNumber(int firstChar) throws ProblemException { - StringBuilder sb = new StringBuilder(); - sb.appendCodePoint(firstChar); - boolean containedDecimalOrE = false; - int c = nextCharRaw(); - while (c != -1 && numberChars.indexOf(c) >= 0) { - if (c == '.' || c == 'e' || c == 'E') - containedDecimalOrE = true; - sb.appendCodePoint(c); - c = nextCharRaw(); - } - // the last character we looked at wasn't part of the number, put it - // back - putBack(c); - String s = sb.toString(); - try { - if (containedDecimalOrE) { - // force floating point representation - return com.gu.typesafe.config.impl.Tokens.newDouble(lineOrigin, Double.parseDouble(s), s); - } else { - // this should throw if the integer is too large for Long - return com.gu.typesafe.config.impl.Tokens.newLong(lineOrigin, Long.parseLong(s), s); - } - } catch (NumberFormatException e) { - // not a number after all, see if it's an unquoted string. - for (char u : s.toCharArray()) { - if (notInUnquotedText.indexOf(u) >= 0) - throw problem(asString(u), "Reserved character '" + asString(u) - + "' is not allowed outside quotes", true /* suggestQuotes */); - } - // no evil chars so we just decide this was a string and - // not a number. - return com.gu.typesafe.config.impl.Tokens.newUnquotedText(lineOrigin, s); - } - } - - private void pullEscapeSequence(StringBuilder sb, StringBuilder sbOrig) throws ProblemException { - int escaped = nextCharRaw(); - if (escaped == -1) - throw problem("End of input but backslash in string had nothing after it"); - - // This is needed so we return the unescaped escape characters back out when rendering - // the token - sbOrig.appendCodePoint('\\'); - sbOrig.appendCodePoint(escaped); - - switch (escaped) { - case '"': - sb.append('"'); - break; - case '\\': - sb.append('\\'); - break; - case '/': - sb.append('/'); - break; - case 'b': - sb.append('\b'); - break; - case 'f': - sb.append('\f'); - break; - case 'n': - sb.append('\n'); - break; - case 'r': - sb.append('\r'); - break; - case 't': - sb.append('\t'); - break; - case 'u': { - // kind of absurdly slow, but screw it for now - char[] a = new char[4]; - for (int i = 0; i < 4; ++i) { - int c = nextCharRaw(); - if (c == -1) - throw problem("End of input but expecting 4 hex digits for \\uXXXX escape"); - a[i] = (char) c; - } - String digits = new String(a); - sbOrig.append(a); - try { - sb.appendCodePoint(Integer.parseInt(digits, 16)); - } catch (NumberFormatException e) { - throw problem(digits, String.format( - "Malformed hex digits after \\u escape in string: '%s'", digits), e); - } - } - break; - default: - throw problem( - asString(escaped), - String.format( - "backslash followed by '%s', this is not a valid escape sequence (quoted strings use JSON escaping, so use double-backslash \\\\ for literal backslash)", - asString(escaped))); - } - } - - private void appendTripleQuotedString(StringBuilder sb, StringBuilder sbOrig) throws ProblemException { - // we are after the opening triple quote and need to consume the - // close triple - int consecutiveQuotes = 0; - for (;;) { - int c = nextCharRaw(); - - if (c == '"') { - consecutiveQuotes += 1; - } else if (consecutiveQuotes >= 3) { - // the last three quotes end the string and the others are - // kept. - sb.setLength(sb.length() - 3); - putBack(c); - break; - } else { - consecutiveQuotes = 0; - if (c == -1) - throw problem("End of input but triple-quoted string was still open"); - else if (c == '\n') { - // keep the line number accurate - lineNumber += 1; - lineOrigin = origin.withLineNumber(lineNumber); - } - } - - sb.appendCodePoint(c); - sbOrig.appendCodePoint(c); - } - } - - private com.gu.typesafe.config.impl.Token pullQuotedString() throws ProblemException { - // the open quote has already been consumed - StringBuilder sb = new StringBuilder(); - - // We need a second string builder to keep track of escape characters. - // We want to return them exactly as they appeared in the original text, - // which means we will need a new StringBuilder to escape escape characters - // so we can also keep the actual value of the string. This is gross. - StringBuilder sbOrig = new StringBuilder(); - sbOrig.appendCodePoint('"'); - - while (true) { - int c = nextCharRaw(); - if (c == -1) - throw problem("End of input but string quote was still open"); - - if (c == '\\') { - pullEscapeSequence(sb, sbOrig); - } else if (c == '"') { - sbOrig.appendCodePoint(c); - break; - } else if (ConfigImplUtil.isC0Control(c)) { - throw problem(asString(c), "JSON does not allow unescaped " + asString(c) - + " in quoted strings, use a backslash escape"); - } else { - sb.appendCodePoint(c); - sbOrig.appendCodePoint(c); - } - } - - // maybe switch to triple-quoted string, sort of hacky... - if (sb.length() == 0) { - int third = nextCharRaw(); - if (third == '"') { - sbOrig.appendCodePoint(third); - appendTripleQuotedString(sb, sbOrig); - } else { - putBack(third); - } - - } - return com.gu.typesafe.config.impl.Tokens.newString(lineOrigin, sb.toString(), sbOrig.toString()); - } - - private com.gu.typesafe.config.impl.Token pullPlusEquals() throws ProblemException { - // the initial '+' has already been consumed - int c = nextCharRaw(); - if (c != '=') { - throw problem(asString(c), "'+' not followed by =, '" + asString(c) - + "' not allowed after '+'", true /* suggestQuotes */); - } - return com.gu.typesafe.config.impl.Tokens.PLUS_EQUALS; - } - - private com.gu.typesafe.config.impl.Token pullSubstitution() throws ProblemException { - // the initial '$' has already been consumed - ConfigOrigin origin = lineOrigin; - int c = nextCharRaw(); - if (c != '{') { - throw problem(asString(c), "'$' not followed by {, '" + asString(c) - + "' not allowed after '$'", true /* suggestQuotes */); - } - - boolean optional = false; - c = nextCharRaw(); - if (c == '?') { - optional = true; - } else { - putBack(c); - } - - WhitespaceSaver saver = new WhitespaceSaver(); - List expression = new ArrayList(); - - com.gu.typesafe.config.impl.Token t; - do { - t = pullNextToken(saver); - - // note that we avoid validating the allowed tokens inside - // the substitution here; we even allow nested substitutions - // in the tokenizer. The parser sorts it out. - if (t == com.gu.typesafe.config.impl.Tokens.CLOSE_CURLY) { - // end the loop, done! - break; - } else if (t == com.gu.typesafe.config.impl.Tokens.END) { - throw problem(origin, - "Substitution ${ was not closed with a }"); - } else { - com.gu.typesafe.config.impl.Token whitespace = saver.check(t, origin, lineNumber); - if (whitespace != null) - expression.add(whitespace); - expression.add(t); - } - } while (true); - - return com.gu.typesafe.config.impl.Tokens.newSubstitution(origin, optional, expression); - } - - private com.gu.typesafe.config.impl.Token pullNextToken(WhitespaceSaver saver) throws ProblemException { - int c = nextCharAfterWhitespace(saver); - if (c == -1) { - return com.gu.typesafe.config.impl.Tokens.END; - } else if (c == '\n') { - // newline tokens have the just-ended line number - com.gu.typesafe.config.impl.Token line = com.gu.typesafe.config.impl.Tokens.newLine(lineOrigin); - lineNumber += 1; - lineOrigin = origin.withLineNumber(lineNumber); - return line; - } else { - com.gu.typesafe.config.impl.Token t; - if (startOfComment(c)) { - t = pullComment(c); - } else { - switch (c) { - case '"': - t = pullQuotedString(); - break; - case '$': - t = pullSubstitution(); - break; - case ':': - t = com.gu.typesafe.config.impl.Tokens.COLON; - break; - case ',': - t = com.gu.typesafe.config.impl.Tokens.COMMA; - break; - case '=': - t = com.gu.typesafe.config.impl.Tokens.EQUALS; - break; - case '{': - t = com.gu.typesafe.config.impl.Tokens.OPEN_CURLY; - break; - case '}': - t = com.gu.typesafe.config.impl.Tokens.CLOSE_CURLY; - break; - case '[': - t = com.gu.typesafe.config.impl.Tokens.OPEN_SQUARE; - break; - case ']': - t = com.gu.typesafe.config.impl.Tokens.CLOSE_SQUARE; - break; - case '+': - t = pullPlusEquals(); - break; - default: - t = null; - break; - } - - if (t == null) { - if (firstNumberChars.indexOf(c) >= 0) { - t = pullNumber(c); - } else if (notInUnquotedText.indexOf(c) >= 0) { - throw problem(asString(c), "Reserved character '" + asString(c) - + "' is not allowed outside quotes", true /* suggestQuotes */); - } else { - putBack(c); - t = pullUnquotedText(); - } - } - } - - if (t == null) - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "bug: failed to generate next token"); - - return t; - } - } - - private static boolean isSimpleValue(com.gu.typesafe.config.impl.Token t) { - if (com.gu.typesafe.config.impl.Tokens.isSubstitution(t) || com.gu.typesafe.config.impl.Tokens.isUnquotedText(t) - || com.gu.typesafe.config.impl.Tokens.isValue(t)) { - return true; - } else { - return false; - } - } - - private void queueNextToken() throws ProblemException { - com.gu.typesafe.config.impl.Token t = pullNextToken(whitespaceSaver); - com.gu.typesafe.config.impl.Token whitespace = whitespaceSaver.check(t, origin, lineNumber); - if (whitespace != null) - tokens.add(whitespace); - - tokens.add(t); - } - - @Override - public boolean hasNext() { - return !tokens.isEmpty(); - } - - @Override - public com.gu.typesafe.config.impl.Token next() { - com.gu.typesafe.config.impl.Token t = tokens.remove(); - if (tokens.isEmpty() && t != com.gu.typesafe.config.impl.Tokens.END) { - try { - queueNextToken(); - } catch (ProblemException e) { - tokens.add(e.problem()); - } - if (tokens.isEmpty()) - throw new ConfigException.BugOrBroken( - "bug: tokens queue should not be empty here"); - } - return t; - } - - @Override - public void remove() { - throw new UnsupportedOperationException( - "Does not make sense to remove items from token stream"); - } - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/Tokens.java b/scripts/src/main/java/com/gu/typesafe/config/impl/Tokens.java deleted file mode 100644 index cf1d311b23..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/Tokens.java +++ /dev/null @@ -1,521 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.List; - -import com.gu.typesafe.config.ConfigException; -import com.gu.typesafe.config.ConfigOrigin; -import com.gu.typesafe.config.ConfigValueType; - -/* FIXME the way the subclasses of Token are private with static isFoo and accessors is kind of ridiculous. */ -final class Tokens { - static private class Value extends Token { - - final private com.gu.typesafe.config.impl.AbstractConfigValue value; - - Value(com.gu.typesafe.config.impl.AbstractConfigValue value) { - this(value, null); - } - - Value(com.gu.typesafe.config.impl.AbstractConfigValue value, String origText) { - super(com.gu.typesafe.config.impl.TokenType.VALUE, value.origin(), origText); - this.value = value; - } - - com.gu.typesafe.config.impl.AbstractConfigValue value() { - return value; - } - - @Override - public String toString() { - if (value().resolveStatus() == ResolveStatus.RESOLVED) - return "'" + value().unwrapped() + "' (" + value.valueType().name() + ")"; - else - return "'' (" + value.valueType().name() + ")"; - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof Value; - } - - @Override - public boolean equals(Object other) { - return super.equals(other) && ((Value) other).value.equals(value); - } - - @Override - public int hashCode() { - return 41 * (41 + super.hashCode()) + value.hashCode(); - } - } - - static private class Line extends Token { - Line(com.gu.typesafe.config.ConfigOrigin origin) { - super(com.gu.typesafe.config.impl.TokenType.NEWLINE, origin); - } - - @Override - public String toString() { - return "'\\n'@" + lineNumber(); - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof Line; - } - - @Override - public boolean equals(Object other) { - return super.equals(other) && ((Line) other).lineNumber() == lineNumber(); - } - - @Override - public int hashCode() { - return 41 * (41 + super.hashCode()) + lineNumber(); - } - - @Override - public String tokenText() { - return "\n"; - } - } - - // This is not a Value, because it requires special processing - static private class UnquotedText extends Token { - final private String value; - - UnquotedText(com.gu.typesafe.config.ConfigOrigin origin, String s) { - super(com.gu.typesafe.config.impl.TokenType.UNQUOTED_TEXT, origin); - this.value = s; - } - - String value() { - return value; - } - - @Override - public String toString() { - return "'" + value + "'"; - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof UnquotedText; - } - - @Override - public boolean equals(Object other) { - return super.equals(other) - && ((UnquotedText) other).value.equals(value); - } - - @Override - public int hashCode() { - return 41 * (41 + super.hashCode()) + value.hashCode(); - } - - @Override - public String tokenText() { - return value; - } - } - - static private class IgnoredWhitespace extends Token { - final private String value; - - IgnoredWhitespace(com.gu.typesafe.config.ConfigOrigin origin, String s) { - super(com.gu.typesafe.config.impl.TokenType.IGNORED_WHITESPACE, origin); - this.value = s; - } - - @Override - public String toString() { return "'" + value + "' (WHITESPACE)"; } - - @Override - protected boolean canEqual(Object other) { - return other instanceof IgnoredWhitespace; - } - - @Override - public boolean equals(Object other) { - return super.equals(other) - && ((IgnoredWhitespace) other).value.equals(value); - } - - @Override - public int hashCode() { - return 41 * (41 + super.hashCode()) + value.hashCode(); - } - - @Override - public String tokenText() { - return value; - } - } - - static private class Problem extends Token { - final private String what; - final private String message; - final private boolean suggestQuotes; - final private Throwable cause; - - Problem(com.gu.typesafe.config.ConfigOrigin origin, String what, String message, boolean suggestQuotes, - Throwable cause) { - super(com.gu.typesafe.config.impl.TokenType.PROBLEM, origin); - this.what = what; - this.message = message; - this.suggestQuotes = suggestQuotes; - this.cause = cause; - } - - String what() { - return what; - } - - String message() { - return message; - } - - boolean suggestQuotes() { - return suggestQuotes; - } - - Throwable cause() { - return cause; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append('\''); - sb.append(what); - sb.append('\''); - sb.append(" ("); - sb.append(message); - sb.append(")"); - return sb.toString(); - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof Problem; - } - - @Override - public boolean equals(Object other) { - return super.equals(other) && ((Problem) other).what.equals(what) - && ((Problem) other).message.equals(message) - && ((Problem) other).suggestQuotes == suggestQuotes - && ConfigImplUtil.equalsHandlingNull(((Problem) other).cause, cause); - } - - @Override - public int hashCode() { - int h = 41 * (41 + super.hashCode()); - h = 41 * (h + what.hashCode()); - h = 41 * (h + message.hashCode()); - h = 41 * (h + Boolean.valueOf(suggestQuotes).hashCode()); - if (cause != null) - h = 41 * (h + cause.hashCode()); - return h; - } - } - - static private abstract class Comment extends Token { - final private String text; - - Comment(com.gu.typesafe.config.ConfigOrigin origin, String text) { - super(com.gu.typesafe.config.impl.TokenType.COMMENT, origin); - this.text = text; - } - - final static class DoubleSlashComment extends Comment { - DoubleSlashComment(com.gu.typesafe.config.ConfigOrigin origin, String text) { - super(origin, text); - } - - @Override - public String tokenText() { - return "//" + super.text; - } - } - - final static class HashComment extends Comment { - HashComment(com.gu.typesafe.config.ConfigOrigin origin, String text) { - super(origin, text); - } - - @Override - public String tokenText() { - return "#" + super.text; - } - } - - String text() { - return text; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("'#"); - sb.append(text); - sb.append("' (COMMENT)"); - return sb.toString(); - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof Comment; - } - - @Override - public boolean equals(Object other) { - return super.equals(other) && ((Comment) other).text.equals(text); - } - - @Override - public int hashCode() { - int h = 41 * (41 + super.hashCode()); - h = 41 * (h + text.hashCode()); - return h; - } - } - - // This is not a Value, because it requires special processing - static private class Substitution extends Token { - final private boolean optional; - final private List value; - - Substitution(com.gu.typesafe.config.ConfigOrigin origin, boolean optional, List expression) { - super(com.gu.typesafe.config.impl.TokenType.SUBSTITUTION, origin); - this.optional = optional; - this.value = expression; - } - - boolean optional() { - return optional; - } - - List value() { - return value; - } - - @Override - public String tokenText() { - return "${" + (this.optional? "?" : "") + Tokenizer.render(this.value.iterator()) + "}"; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - for (Token t : value) { - sb.append(t.toString()); - } - return "'${" + sb.toString() + "}'"; - } - - @Override - protected boolean canEqual(Object other) { - return other instanceof Substitution; - } - - @Override - public boolean equals(Object other) { - return super.equals(other) - && ((Substitution) other).value.equals(value); - } - - @Override - public int hashCode() { - return 41 * (41 + super.hashCode()) + value.hashCode(); - } - } - - static boolean isValue(Token token) { - return token instanceof Value; - } - - static com.gu.typesafe.config.impl.AbstractConfigValue getValue(Token token) { - if (token instanceof Value) { - return ((Value) token).value(); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "tried to get value of non-value token " + token); - } - } - - static boolean isValueWithType(Token t, ConfigValueType valueType) { - return isValue(t) && getValue(t).valueType() == valueType; - } - - static boolean isNewline(Token token) { - return token instanceof Line; - } - - static boolean isProblem(Token token) { - return token instanceof Problem; - } - - static String getProblemWhat(Token token) { - if (token instanceof Problem) { - return ((Problem) token).what(); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("tried to get problem what from " + token); - } - } - - static String getProblemMessage(Token token) { - if (token instanceof Problem) { - return ((Problem) token).message(); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("tried to get problem message from " + token); - } - } - - static boolean getProblemSuggestQuotes(Token token) { - if (token instanceof Problem) { - return ((Problem) token).suggestQuotes(); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("tried to get problem suggestQuotes from " - + token); - } - } - - static Throwable getProblemCause(Token token) { - if (token instanceof Problem) { - return ((Problem) token).cause(); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("tried to get problem cause from " + token); - } - } - - static boolean isComment(Token token) { - return token instanceof Comment; - } - - static String getCommentText(Token token) { - if (token instanceof Comment) { - return ((Comment) token).text(); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken("tried to get comment text from " + token); - } - } - - static boolean isUnquotedText(Token token) { - return token instanceof UnquotedText; - } - - static String getUnquotedText(Token token) { - if (token instanceof UnquotedText) { - return ((UnquotedText) token).value(); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "tried to get unquoted text from " + token); - } - } - - static boolean isIgnoredWhitespace(Token token) { - return token instanceof IgnoredWhitespace; - } - - static boolean isSubstitution(Token token) { - return token instanceof Substitution; - } - - static List getSubstitutionPathExpression(Token token) { - if (token instanceof Substitution) { - return ((Substitution) token).value(); - } else { - throw new com.gu.typesafe.config.ConfigException.BugOrBroken( - "tried to get substitution from " + token); - } - } - - static boolean getSubstitutionOptional(Token token) { - if (token instanceof Substitution) { - return ((Substitution) token).optional(); - } else { - throw new ConfigException.BugOrBroken("tried to get substitution optionality from " - + token); - } - } - - final static Token START = Token.newWithoutOrigin(com.gu.typesafe.config.impl.TokenType.START, "start of file", ""); - final static Token END = Token.newWithoutOrigin(com.gu.typesafe.config.impl.TokenType.END, "end of file", ""); - final static Token COMMA = Token.newWithoutOrigin(com.gu.typesafe.config.impl.TokenType.COMMA, "','", ","); - final static Token EQUALS = Token.newWithoutOrigin(com.gu.typesafe.config.impl.TokenType.EQUALS, "'='", "="); - final static Token COLON = Token.newWithoutOrigin(com.gu.typesafe.config.impl.TokenType.COLON, "':'", ":"); - final static Token OPEN_CURLY = Token.newWithoutOrigin(com.gu.typesafe.config.impl.TokenType.OPEN_CURLY, "'{'", "{"); - final static Token CLOSE_CURLY = Token.newWithoutOrigin(com.gu.typesafe.config.impl.TokenType.CLOSE_CURLY, "'}'", "}"); - final static Token OPEN_SQUARE = Token.newWithoutOrigin(com.gu.typesafe.config.impl.TokenType.OPEN_SQUARE, "'['", "["); - final static Token CLOSE_SQUARE = Token.newWithoutOrigin(com.gu.typesafe.config.impl.TokenType.CLOSE_SQUARE, "']'", "]"); - final static Token PLUS_EQUALS = Token.newWithoutOrigin(com.gu.typesafe.config.impl.TokenType.PLUS_EQUALS, "'+='", "+="); - - static Token newLine(com.gu.typesafe.config.ConfigOrigin origin) { - return new Line(origin); - } - - static Token newProblem(com.gu.typesafe.config.ConfigOrigin origin, String what, String message, - boolean suggestQuotes, Throwable cause) { - return new Problem(origin, what, message, suggestQuotes, cause); - } - - static Token newCommentDoubleSlash(com.gu.typesafe.config.ConfigOrigin origin, String text) { - return new Comment.DoubleSlashComment(origin, text); - } - - static Token newCommentHash(com.gu.typesafe.config.ConfigOrigin origin, String text) { - return new Comment.HashComment(origin, text); - } - - static Token newUnquotedText(com.gu.typesafe.config.ConfigOrigin origin, String s) { - return new UnquotedText(origin, s); - } - - static Token newIgnoredWhitespace(com.gu.typesafe.config.ConfigOrigin origin, String s) { - return new IgnoredWhitespace(origin, s); - } - - static Token newSubstitution(com.gu.typesafe.config.ConfigOrigin origin, boolean optional, List expression) { - return new Substitution(origin, optional, expression); - } - - static Token newValue(com.gu.typesafe.config.impl.AbstractConfigValue value) { - return new Value(value); - } - static Token newValue(com.gu.typesafe.config.impl.AbstractConfigValue value, String origText) { - return new Value(value, origText); - } - - static Token newString(com.gu.typesafe.config.ConfigOrigin origin, String value, String origText) { - return newValue(new ConfigString.Quoted(origin, value), origText); - } - - static Token newInt(com.gu.typesafe.config.ConfigOrigin origin, int value, String origText) { - return newValue(com.gu.typesafe.config.impl.ConfigNumber.newNumber(origin, value, - origText), origText); - } - - static Token newDouble(com.gu.typesafe.config.ConfigOrigin origin, double value, - String origText) { - return newValue(com.gu.typesafe.config.impl.ConfigNumber.newNumber(origin, value, - origText), origText); - } - - static Token newLong(com.gu.typesafe.config.ConfigOrigin origin, long value, String origText) { - return newValue(com.gu.typesafe.config.impl.ConfigNumber.newNumber(origin, value, - origText), origText); - } - - static Token newNull(com.gu.typesafe.config.ConfigOrigin origin) { - return newValue(new com.gu.typesafe.config.impl.ConfigNull(origin), "null"); - } - - static Token newBoolean(ConfigOrigin origin, boolean value) { - return newValue(new ConfigBoolean(origin, value), "" + value); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/Unmergeable.java b/scripts/src/main/java/com/gu/typesafe/config/impl/Unmergeable.java deleted file mode 100644 index 443cc5d312..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/Unmergeable.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (C) 2011-2012 Typesafe Inc. - */ -package com.gu.typesafe.config.impl; - -import java.util.Collection; - -/** - * Interface that tags a ConfigValue that is not mergeable until after - * substitutions are resolved. Basically these are special ConfigValue that - * never appear in a resolved tree, like {@link ConfigSubstitution} and - * {@link ConfigDelayedMerge}. - */ -interface Unmergeable { - Collection unmergedValues(); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/impl/package.html b/scripts/src/main/java/com/gu/typesafe/config/impl/package.html deleted file mode 100644 index 52592b0dba..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/impl/package.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - -

-Internal implementation details that can change ABI at any time. -

- -

-Please check out the {@link com.typesafe.config.Config public API} instead, unless -you're interested in browsing implementation details. None of the ABI -under impl has any guarantees; it will change whenever someone -feels like changing it. If you feel you need access to something -in impl, please -file a feature request. -

- - - diff --git a/scripts/src/main/java/com/gu/typesafe/config/package.html b/scripts/src/main/java/com/gu/typesafe/config/package.html deleted file mode 100644 index 8f5d9f4547..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/package.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - -

-An API for loading and using configuration files, see the project site -for more information. -

- -

-Typically you would load configuration with a static method from {@link com.typesafe.config.ConfigFactory} and then use -it with methods in the {@link com.typesafe.config.Config} interface. Configuration may be in the form of JSON files, -Java properties, or HOCON files; you may also -build your own configuration in code or from your own file formats. -

- -

-An application can simply call {@link com.typesafe.config.ConfigFactory#load()} and place -its configuration in "application.conf" on the classpath. -If you use the default configuration from {@link com.typesafe.config.ConfigFactory#load()} -there's no need to pass a configuration to your libraries -and frameworks, as long as they all default to this same default, which they should. -
Example application code: Java and Scala. -
Showing a couple of more special-purpose features, a more complex example: Java and Scala. -

- -

-A library or framework should ship a file "reference.conf" in its jar, and allow an application to pass in a -{@link com.typesafe.config.Config} to be used for the library. If no {@link com.typesafe.config.Config} is provided, -call {@link com.typesafe.config.ConfigFactory#load()} -to get the default one. Typically a library might offer two constructors, one with a Config parameter -and one which uses {@link com.typesafe.config.ConfigFactory#load()}. -
Example library code: Java and Scala. -

- -

-Check out the full examples directory on GitHub. -

- -

-What else to read: -

    -
  • The overview documentation for interface {@link com.typesafe.config.Config}.
  • -
  • The README for the library.
  • -
  • If you want to use .conf files in addition to .json and .properties, - see the README for some short examples - and the full HOCON spec for the long version.
  • -
-

- - - diff --git a/scripts/src/main/java/com/gu/typesafe/config/parser/ConfigDocument.java b/scripts/src/main/java/com/gu/typesafe/config/parser/ConfigDocument.java deleted file mode 100644 index 6b04f8f708..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/parser/ConfigDocument.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.gu.typesafe.config.parser; - -import com.gu.typesafe.config.ConfigValue; - -/** - * Represents an individual HOCON or JSON file, preserving all - * formatting and syntax details. This can be used to replace - * individual values and exactly render the original text of the - * input. - * - *

- * Because this object is immutable, it is safe to use from multiple threads and - * there's no need for "defensive copies." - * - *

- * Do not implement interface {@code ConfigDocument}; it should only be - * implemented by the config library. Arbitrary implementations will not work - * because the library internals assume a specific concrete implementation. - * Also, this interface is likely to grow new methods over time, so third-party - * implementations will break. - */ -public interface ConfigDocument { - /** - * Returns a new ConfigDocument that is a copy of the current ConfigDocument, - * but with the desired value set at the desired path. If the path exists, it will - * remove all duplicates before the final occurrence of the path, and replace the value - * at the final occurrence of the path. If the path does not exist, it will be added. If - * the document has an array as the root value, an exception will be thrown. - * - * @param path the path at which to set the desired value - * @param newValue the value to set at the desired path, represented as a string. This - * string will be parsed into a ConfigNode using the same options used to - * parse the entire document, and the text will be inserted - * as-is into the document. Leading and trailing comments, whitespace, or - * newlines are not allowed, and if present an exception will be thrown. - * If a concatenation is passed in for newValue but the document was parsed - * with JSON, the first value in the concatenation will be parsed and inserted - * into the ConfigDocument. - * @return a copy of the ConfigDocument with the desired value at the desired path - */ - ConfigDocument withValueText(String path, String newValue); - - /** - * Returns a new ConfigDocument that is a copy of the current - * ConfigDocument, but with the desired value set at the - * desired path. Works like {@link #withValueText(String, String)}, - * but takes a ConfigValue instead of a string. - * - * @param path the path at which to set the desired value - * @param newValue the value to set at the desired path, represented as a ConfigValue. - * The rendered text of the ConfigValue will be inserted into the - * ConfigDocument. - * @return a copy of the ConfigDocument with the desired value at the desired path - */ - ConfigDocument withValue(String path, ConfigValue newValue); - - /** - * Returns a new ConfigDocument that is a copy of the current ConfigDocument, but with - * all values at the desired path removed. If the path does not exist in the document, - * a copy of the current document will be returned. If there is an array at the root, an exception - * will be thrown. - * - * @param path the path to remove from the document - * @return a copy of the ConfigDocument with the desired value removed from the document. - */ - ConfigDocument withoutPath(String path); - - /** - * Returns a boolean indicating whether or not a ConfigDocument has a value at the desired path. - * null counts as a value for purposes of this check. - * @param path the path to check - * @return true if the path exists in the document, otherwise false - */ - boolean hasPath(String path); - - /** - * The original text of the input, modified if necessary with - * any replaced or added values. - * @return the modified original text - */ - String render(); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/parser/ConfigDocumentFactory.java b/scripts/src/main/java/com/gu/typesafe/config/parser/ConfigDocumentFactory.java deleted file mode 100644 index 88801c5aad..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/parser/ConfigDocumentFactory.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.gu.typesafe.config.parser; - -import com.gu.typesafe.config.ConfigException; - -import com.gu.typesafe.config.ConfigParseOptions; -import com.gu.typesafe.config.impl.Parseable; - -import java.io.File; -import java.io.Reader; - -/** - * Factory for creating {@link - * ConfigDocument} instances. - */ -public final class ConfigDocumentFactory { - - /** - * Parses a Reader into a ConfigDocument instance. - * - * @param reader - * the reader to parse - * @param options - * parse options to control how the reader is interpreted - * @return the parsed configuration - * @throws ConfigException on IO or parse errors - */ - public static ConfigDocument parseReader(Reader reader, com.gu.typesafe.config.ConfigParseOptions options) { - return Parseable.newReader(reader, options).parseConfigDocument(); - } - - /** - * Parses a reader into a Config instance as with - * {@link #parseReader(Reader, com.gu.typesafe.config.ConfigParseOptions)} but always uses the - * default parse options. - * - * @param reader - * the reader to parse - * @return the parsed configuration - * @throws ConfigException on IO or parse errors - */ - public static ConfigDocument parseReader(Reader reader) { - return parseReader(reader, com.gu.typesafe.config.ConfigParseOptions.defaults()); - } - - /** - * Parses a file into a ConfigDocument instance. - * - * @param file - * the file to parse - * @param options - * parse options to control how the file is interpreted - * @return the parsed configuration - * @throws ConfigException on IO or parse errors - */ - public static ConfigDocument parseFile(File file, com.gu.typesafe.config.ConfigParseOptions options) { - return Parseable.newFile(file, options).parseConfigDocument(); - } - - /** - * Parses a file into a ConfigDocument instance as with - * {@link #parseFile(File, com.gu.typesafe.config.ConfigParseOptions)} but always uses the - * default parse options. - * - * @param file - * the file to parse - * @return the parsed configuration - * @throws ConfigException on IO or parse errors - */ - public static ConfigDocument parseFile(File file) { - return parseFile(file, com.gu.typesafe.config.ConfigParseOptions.defaults()); - } - - /** - * Parses a string which should be valid HOCON or JSON. - * - * @param s string to parse - * @param options parse options - * @return the parsed configuration - */ - public static ConfigDocument parseString(String s, com.gu.typesafe.config.ConfigParseOptions options) { - return Parseable.newString(s, options).parseConfigDocument(); - } - - /** - * Parses a string (which should be valid HOCON or JSON). Uses the - * default parse options. - * - * @param s string to parse - * @return the parsed configuration - */ - public static ConfigDocument parseString(String s) { - return parseString(s, ConfigParseOptions.defaults()); - } -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/parser/ConfigNode.java b/scripts/src/main/java/com/gu/typesafe/config/parser/ConfigNode.java deleted file mode 100644 index f47187737c..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/parser/ConfigNode.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (C) 2015 Typesafe Inc. - */ -package com.gu.typesafe.config.parser; - -/** - * A node in the syntax tree for a HOCON or JSON document. - * - *

- * Note: at present there is no way to obtain an instance of this interface, so - * please ignore it. A future release will make syntax tree nodes available in - * the public API. If you are interested in working on it, please see: https://github.com/lightbend/config/issues/300 - * - *

- * Because this object is immutable, it is safe to use from multiple threads and - * there's no need for "defensive copies." - * - *

- * Do not implement interface {@code ConfigNode}; it should only be - * implemented by the config library. Arbitrary implementations will not work - * because the library internals assume a specific concrete implementation. - * Also, this interface is likely to grow new methods over time, so third-party - * implementations will break. - */ -public interface ConfigNode { - /** - * The original text of the input which was used to form this particular - * node. - * - * @return the original text used to form this node as a String - */ - public String render(); -} diff --git a/scripts/src/main/java/com/gu/typesafe/config/parser/package.html b/scripts/src/main/java/com/gu/typesafe/config/parser/package.html deleted file mode 100644 index b0e7b3cea4..0000000000 --- a/scripts/src/main/java/com/gu/typesafe/config/parser/package.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - -

-This package supplies a raw parser and syntax tree for individual HOCON and JSON -files. You do not want this package for everyday config in your app: see -the com.typesafe.config package instead. You would use the raw -parser if you're doing something like reading, modifying, and re-saving a config -file. For info on the main config API this parser is a part of, -see the project site. -

- -

- For working with the raw syntax tree, some important classes are: -

    -
  • {@link com.typesafe.config.parser.ConfigDocument} - a loaded HOCON - or JSON document
  • -
  • {@link com.typesafe.config.parser.ConfigDocumentFactory} - - static methods to instantiate a document
  • -
  • {@link com.typesafe.config.parser.ConfigNode} - syntax node - in a document
  • -
-

- - - diff --git a/scripts/src/main/scala/com/gu/mediaservice/lib/JsonValueCodecJsValue.scala b/scripts/src/main/scala/com/gu/mediaservice/lib/JsonValueCodecJsValue.scala deleted file mode 100644 index 0540dcdc85..0000000000 --- a/scripts/src/main/scala/com/gu/mediaservice/lib/JsonValueCodecJsValue.scala +++ /dev/null @@ -1,107 +0,0 @@ -package com.gu.mediaservice.lib - -import com.github.plokhotnyuk.jsoniter_scala.core.{JsonReader, JsonValueCodec, JsonWriter} -import _root_.play.api.libs.json.{JsArray, JsBoolean, JsFalse, JsNull, JsNumber, JsObject, JsString, JsTrue, JsValue} - -import scala.collection.IndexedSeq - -object JsonValueCodecJsValue { - - implicit def jsValueCodec: JsonValueCodec[JsValue] = { - new JsonValueCodec[JsValue] { - - /** - * The implementation was borrowed from: https://github.com/plokhotnyuk/jsoniter-scala/blob/e80d51019b39efacff9e695de97dce0c23ae9135/jsoniter-scala-benchmark/src/main/scala/io/circe/CirceJsoniter.scala - * and adapted to meet PlayJson criteria. - */ - def decodeValue(in: JsonReader, default: JsValue): JsValue = { - val b = in.nextToken() - if (b == 'n') in.readNullOrError(default, "expected `null` value") - else if (b == '"') { - in.rollbackToken() - JsString(in.readString(null)) - } else if (b == 'f' || b == 't') { - in.rollbackToken() - if (in.readBoolean()) JsTrue else JsFalse - } else if ((b >= '0' && b <= '9') || b == '-') { - in.rollbackToken() - val bigDecimal = in.readBigDecimal(null) - JsNumber(bigDecimal) - } else if (b == '[') { - val array: IndexedSeq[JsValue] = - if (in.isNextToken(']')) new Array[JsValue](0) - else { - in.rollbackToken() - var i = 0 - var arr = new Array[JsValue](4) - do { - if (i == arr.length) arr = java.util.Arrays.copyOf(arr, i << 1) - arr(i) = decodeValue(in, default) - i += 1 - } while (in.isNextToken(',')) - - if (in.isCurrentToken(']')) - if (i == arr.length) arr else java.util.Arrays.copyOf(arr, i) - else in.arrayEndOrCommaError() - } - JsArray(array) - } else if (b == '{') { - /* - * Because of DoS vulnerability in Scala 2.12 HashMap https://github.com/scala/bug/issues/11203 - * we use a Java LinkedHashMap because it better handles hash code collisions for Comparable keys. - */ - val kvs = - if (in.isNextToken('}')) new java.util.LinkedHashMap[String, JsValue]() - else { - val underlying = new java.util.LinkedHashMap[String, JsValue]() - in.rollbackToken() - do { - underlying.put(in.readKeyAsString(), decodeValue(in, default)) - } while (in.isNextToken(',')) - - if (!in.isCurrentToken('}')) - in.objectEndOrCommaError() - - underlying - } - import scala.jdk.CollectionConverters._ - JsObject(kvs.asScala) - } else { - in.decodeError("expected JSON value") - } - } - - def encodeValue(jsValue: JsValue, out: JsonWriter): Unit = { - jsValue match { - case JsBoolean(b) => - out.writeVal(b) - case JsFalse => - out.writeVal(false) - case JsTrue => - out.writeVal(true) - case JsString(value) => - out.writeVal(value) - case JsNumber(value) => - out.writeVal(value) - case JsArray(items) => - out.writeArrayStart() - items.foreach(encodeValue(_, out)) - out.writeArrayEnd() - case JsObject(kvs) => - out.writeObjectStart() - kvs.foreach { - case (k, v) => - out.writeKey(k) - encodeValue(v, out) - } - out.writeObjectEnd() - case JsNull => - out.writeNull() - } - } - - val nullValue: JsValue = JsNull - } - } - -} diff --git a/scripts/src/main/scala/com/gu/mediaservice/lib/UserCredentials.scala b/scripts/src/main/scala/com/gu/mediaservice/lib/UserCredentials.scala deleted file mode 100644 index 0a3d7a2b0e..0000000000 --- a/scripts/src/main/scala/com/gu/mediaservice/lib/UserCredentials.scala +++ /dev/null @@ -1,25 +0,0 @@ -package com.gu.mediaservice.lib - -import java.nio.file.Paths -import java.util.Properties -import java.io.{File, FileInputStream} -import scala.jdk.CollectionConverters._ -import com.amazonaws.auth.{BasicAWSCredentials, AWSCredentials} - -object UserCredentials { - - def awsCredentials: AWSCredentials = { - val path = sys.env.getOrElse("AWS_CREDENTIAL_FILE", sys.error("Required environment variable AWS_CREDENTIAL_FILE is missing")) - val file = Paths.get(path).toFile - println(s"Reading AWS credentials from $path") - val props = properties(file) - new BasicAWSCredentials(props("AWSAccessKeyId"), props("AWSSecretKey")) - } - - def properties(file: File): Map[String, String] = { - val props = new Properties() - props.load(new FileInputStream(file)) - props.asScala.toMap - } - -} diff --git a/scripts/src/main/scala/com/gu/mediaservice/scripts/BackfillEditLastModified.scala b/scripts/src/main/scala/com/gu/mediaservice/scripts/BackfillEditLastModified.scala deleted file mode 100644 index 881d532857..0000000000 --- a/scripts/src/main/scala/com/gu/mediaservice/scripts/BackfillEditLastModified.scala +++ /dev/null @@ -1,188 +0,0 @@ -package com.gu.mediaservice.scripts - -import com.sksamuel.elastic4s.ElasticApi.matchQuery -import com.sksamuel.elastic4s.ElasticDsl._ -import org.joda.time.DateTime -import play.api.libs.json.Json -import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider -import software.amazon.awssdk.regions.Region -import software.amazon.awssdk.services.dynamodb.model.{AttributeValue, ScanRequest, UpdateItemRequest} -import software.amazon.awssdk.services.dynamodb.{DynamoDbClient, DynamoDbClientBuilder} -import software.amazon.awssdk.services.s3.S3Client - -import java.io.{File, FileOutputStream, PrintWriter} -import scala.jdk.CollectionConverters._ -import scala.io.Source -import scala.util.Try - -object BackfillEditLastModified extends EsScript { - - private val profile = "media-service" - private val region = Region.EU_WEST_1 - private val credentials = DefaultCredentialsProvider.builder.profileName(profile).build - private val dynamo: DynamoDbClient = DynamoDbClient.builder - .region(region) - .credentialsProvider(credentials) - .build - - private var total = 0 - private var skipped = 0 - private var updated = 0 - - override def run(esUrl: String, args: List[String]): Unit = { - def option(options: List[String], argName: String): (Option[String], List[String]) = { - val arg = options.find(_.startsWith(s"--$argName=")) - val value = arg.map(_.stripPrefix(s"--$argName=")) - value -> options.filterNot(arg.contains) - } - - args match { - case dynamoTable :: fileNamePrefix :: remainder => - val (maybeRun, _) = option(remainder, "run") - val dryRun = !maybeRun.contains("true") - val esClient = new EsClient(esUrl) - try { - backfill(esClient, dynamoTable, fileNamePrefix, dryRun) - } finally { - esClient.client.close() - } - case _ => throw new IllegalArgumentException("Usage: BackfillEditLastModified [--run=true]") - } - } - - def backfill(esClient: EsClient, dynamoTable: String, fileName: String, dryRun: Boolean): Unit = { - val lastFile = new File(s"$fileName.last") - val lastEvaluatedKey = if (lastFile.exists()) { - val source = Source.fromFile(lastFile) - val lastId = try { - source.getLines().toList match { - case id :: totalStr :: _ => - total = totalStr.toInt - Some(id) - case _ => None - } - } finally { - source.close() - } - lastId.map { id => - Map("id" -> AttributeValue.builder.s(id).build).asJava - } - } else None - - val logWriter = new PrintWriter(new FileOutputStream(s"$fileName.$total.log")) - def log(msg: String) = logWriter.println(s"[${DateTime.now}/($total/U:$updated/S:$skipped]: $msg") - - log(s"Starting backfill of $dynamoTable from ${esClient.url}; ${if(dryRun){"DRYRUN ONLY"}else{"RUNNING"}}") - log(s"Last evaluated key: $lastEvaluatedKey") - // scan through edits table (this is the shorter list by far) - val scanIterator = dynamo.scanPaginator( - ScanRequest.builder - .tableName(dynamoTable) - .projectionExpression("id") - .limit(500) - .exclusiveStartKey(lastEvaluatedKey.orNull) - .build - ) - scanIterator - .iterator().asScala - .map { r => - - val lastEvaluatedKey = Some(r.lastEvaluatedKey.asScala) - .filter(_ => r.hasLastEvaluatedKey) - .flatMap(_.get("id")) - .map(_.s()) - - - lastEvaluatedKey -> r.items.asScala - } - .map { case (lek, records) => - lek -> records.flatMap(_.asScala.get("id").map(_.s)) - } - .foreach { case (lek, imageIds) => - imageIds.foreach { id => - // for each edit entry: - // do a simple get on the ES cluster - val maybeLastModified = getUserMetadataLastModified(esClient, id) - // update just the last modified value on the edits table - maybeLastModified match { - case Left(reason) => - skipped += 1 - log(s"$id: Unable to get userMetadataLastModified - $reason") - case Right(lastModified) => - updated += 1 - val result = updateEditRecord(dynamoTable, id, lastModified, dryRun) - log(s"$id: $result") - } - total += 1 - Thread.sleep(10) - } - - lek match { - case Some(id) => - log(s"Last evaluated key $id (batch of ${imageIds.size})") - val logWriter = new PrintWriter(new FileOutputStream(lastFile)) - try { - logWriter.println(id) - logWriter.println(total) - } finally { - logWriter.close() - } - case None => // nowt - } - - } - log(s"Finished. Updated: $updated; Skipped: $skipped") - } - - def getUserMetadataLastModified(esClient: EsClient, id: String): Either[String, DateTime] = { - val client = esClient.client - val index = esClient.currentIndex - val queryType = matchQuery("id", id) - val queryResponse = client.execute({ - search(index) - .query(queryType) - .fetchSource(false) - .sourceInclude( - "id", - "userMetadataLastModified" - ) - }).await - - queryResponse.status match { - case 200 => queryResponse.result.hits.hits.flatMap{ hit => - (Json.parse(hit.sourceAsString) \ "userMetadataLastModified").asOpt[String] - } - .headOption - .toRight("Not in ES document") - .flatMap { lm => - Try(new DateTime(lm)).toEither.left.map { t => - t.toString - } - } - case _ => - client.close() - throw new Exception("Failed performing search query") - } - } - - def updateEditRecord(tableName: String, id: String, lastModified: DateTime, dryRun: Boolean): String = { - val request = UpdateItemRequest.builder - .tableName(tableName) - .key(Map("id" -> AttributeValue.builder.s(id).build).asJava) - .updateExpression("SET lastModified = :lastModified") - .expressionAttributeValues(Map(":lastModified" -> AttributeValue.builder.s(lastModified.toString).build).asJava) - .build - val msg = s"Updated with $lastModified ($request)" - if (dryRun) { - s"Would: $msg" - } else { - dynamo.updateItem(request) - msg - } - } - - override def usageError: Nothing = { - System.err.println("Usage: BackfillEditLastModified [--run=true]") - sys.exit(1) - } -} diff --git a/scripts/src/main/scala/com/gu/mediaservice/scripts/BucketMetadata.scala b/scripts/src/main/scala/com/gu/mediaservice/scripts/BucketMetadata.scala deleted file mode 100644 index 1949018042..0000000000 --- a/scripts/src/main/scala/com/gu/mediaservice/scripts/BucketMetadata.scala +++ /dev/null @@ -1,68 +0,0 @@ -package com.gu.mediaservice.scripts - - -import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream -import org.joda.time.DateTime -import play.api.libs.json.{Json, OFormat} -import software.amazon.awssdk.auth.credentials.{DefaultCredentialsProvider, ProfileCredentialsProvider} -import software.amazon.awssdk.regions.Region -import software.amazon.awssdk.services.s3.S3Client -import software.amazon.awssdk.services.s3.model.{HeadObjectRequest, ListObjectsV2Request} - -import java.io.{BufferedWriter, File, FileOutputStream, FileWriter, OutputStreamWriter} -import java.time.Instant -import scala.jdk.CollectionConverters._ - -case class ObjectMetadata(key: String, lastModified: Instant, metadata: Map[String, String]) -object ObjectMetadata { - implicit val format: OFormat[ObjectMetadata] = Json.format[ObjectMetadata] -} - -/** - * Dump selected metadata for all objects in a bucket. - * Given a bucket and an output file this will create a JSON line per object containing the key, lastModified time and - * user metadata. - */ -object BucketMetadata { - def apply(args: List[String]): Unit = { - args match { - case bucketName :: fileName :: Nil => bucketMetadata(bucketName, new File(fileName)) - case _ => throw new IllegalArgumentException("Usage: BucketMetadata ") - } - } - - def bucketMetadata(bucketName: String, outputFile: File) = { - val fileOutputStream = new FileOutputStream(outputFile) - val compressOutputStream = new BZip2CompressorOutputStream(fileOutputStream) - val sw = new OutputStreamWriter(compressOutputStream) - System.err.println(s"Output encoding: ${sw.getEncoding}") - val stream = new BufferedWriter(sw) - - try { - val s3: S3Client = S3Client.builder - .region(Region.EU_WEST_1) - .credentialsProvider(DefaultCredentialsProvider.builder.profileName("media-service").build) - .build - - val results = s3.listObjectsV2Paginator(ListObjectsV2Request.builder.bucket(bucketName).build) - results.iterator().asScala.flatMap { results => - results.contents().asScala - }.map { s3Object => - val headObjectResponse = s3.headObject(HeadObjectRequest.builder.bucket(bucketName).key(s3Object.key).build) - s3Object -> headObjectResponse - }.map { case (s3Object, metadata) => - ObjectMetadata(s3Object.key, metadata.lastModified, metadata.metadata.asScala.toMap) - }.map { md => - Json.stringify(Json.toJson(md)) - }.zipWithIndex - .foreach { case (json, idx) => - if (idx % 1000 == 0) System.err.println(s"${DateTime.now.toString}: ${idx}") - stream.write(json) - stream.newLine() - stream.flush() - } - } finally { - stream.close() - } - } -} diff --git a/scripts/src/main/scala/com/gu/mediaservice/scripts/ConvertConfig.scala b/scripts/src/main/scala/com/gu/mediaservice/scripts/ConvertConfig.scala deleted file mode 100644 index 231595e531..0000000000 --- a/scripts/src/main/scala/com/gu/mediaservice/scripts/ConvertConfig.scala +++ /dev/null @@ -1,65 +0,0 @@ -package com.gu.mediaservice.scripts - -import java.io.File -import java.nio.charset.StandardCharsets -import java.nio.file.{Files, Path, Paths} - -import com.gu.typesafe.config.{ConfigFactory, ConfigRenderOptions} - -import scala.util.Try - -import scala.jdk.CollectionConverters._ - -object ConvertConfig { - case class Conversion(input: File, output: File) - - def apply(args: List[String]): Unit = { - args match { - case "-f" :: as => convertConfigs(as, overwrite = true) - case as => convertConfigs(as, overwrite = false) - } - } - - private def convertConfigs(args: List[String], overwrite: Boolean): Unit = { - val conversions = args.flatMap { arg => - val input = new File(arg) - assert(input.exists, s"File provided for conversion doesn't exist: $input") - val files: List[File] = if (input.isDirectory) { - val paths: List[Path] = Files.walk(input.toPath).iterator.asScala.toList - val regularFiles = paths.filter(file => Files.isRegularFile(file)) - val propertiesFiles = regularFiles.filter(_.toString.endsWith(".properties")) - val files = propertiesFiles.map(_.toFile) - assert(files.nonEmpty, s"No properties files found to convert in $input") - files - } else { - assert(input.toString.endsWith(".properties"), s"File provided for conversion isn't a java properties input: $input") - List(input) - } - files.map { f => - val output = new File(s"${f.toString.stripSuffix(".properties")}.conf") - if (!overwrite) { - assert(!output.exists, s"Output file for $input already exists: $output") - } - Conversion(f, output) - } - } - - conversions.foreach(conversion => convert(conversion)) - } - - private def convert(conversion: Conversion): Either[Throwable, Unit] = { - for { - config <- Try(ConfigFactory.parseFile(conversion.input)).toEither - options = ConfigRenderOptions.defaults().setOriginComments(false).setJson(false).setCompactKeys(true) - hoconStr = config.root().render(options) - _ <- writeToFile(conversion.output, hoconStr) - } yield () - } - - private def writeToFile(file: File, str: String): Either[Throwable, Path] = { - Try{ - System.err.println(s"Writing config to $file") - Files.write(file.toPath, str.getBytes(StandardCharsets.UTF_8)) - }.toEither - } -} diff --git a/scripts/src/main/scala/com/gu/mediaservice/scripts/DecodeComparator.scala b/scripts/src/main/scala/com/gu/mediaservice/scripts/DecodeComparator.scala deleted file mode 100644 index 6d2113dcb1..0000000000 --- a/scripts/src/main/scala/com/gu/mediaservice/scripts/DecodeComparator.scala +++ /dev/null @@ -1,39 +0,0 @@ -package com.gu.mediaservice.scripts - -import com.gu.mediaservice.lib.net.URI -import play.api.libs.json.{JsError, JsSuccess, Json} - -import java.io.File -import scala.io.Source - -/** - * Given a JSON lines file output from BucketMetadata this will verify that metadata is not changed when being - * passed through a URI decode function. This was to check that it is safe to deploy - * https://github.com/guardian/grid/pull/3165 - */ -object DecodeComparator { - def apply(args: List[String]): Unit = { - args match { - case fileName :: Nil => compare(new File(fileName)) - case as => throw new IllegalArgumentException("Usage: DecodeComparator ") - } - } - - def compare(file: File): Unit = { - val source = Source.fromFile(file) - try { - source.getLines().foreach { line => - Json.fromJson[ObjectMetadata](Json.parse(line)) match { - case JsError(errors) => System.err.println(s"Couldn't parse JSON $line") - case JsSuccess(metadata, _) => - metadata.metadata.toList.foreach{ case (key, value) => - val decodedValue = URI.decode(value) - if (value != decodedValue) System.out.println(s"Difference between $key '$value' and '$decodedValue'") - } - } - } - } finally { - source.close() - } - } -} diff --git a/scripts/src/main/scala/com/gu/mediaservice/scripts/EnactS3Changes.scala b/scripts/src/main/scala/com/gu/mediaservice/scripts/EnactS3Changes.scala deleted file mode 100644 index c728ca354e..0000000000 --- a/scripts/src/main/scala/com/gu/mediaservice/scripts/EnactS3Changes.scala +++ /dev/null @@ -1,200 +0,0 @@ -package com.gu.mediaservice.scripts - -import java.io._ -import com.amazonaws.auth.profile.ProfileCredentialsProvider -import org.apache.commons.compress.compressors.bzip2.{BZip2CompressorInputStream, BZip2CompressorOutputStream} -import play.api.libs.json.{JsObject, JsValue, Json, OWrites} - -import scala.io.Source -import com.amazonaws.services.s3.AmazonS3 -import com.amazonaws.services.s3.AmazonS3ClientBuilder -import com.amazonaws.services.s3.model.AmazonS3Exception -import org.joda.time.{DateTime, Duration} -import com.gu.mediaservice.lib.JsonValueCodecJsValue.jsValueCodec -import com.github.plokhotnyuk.jsoniter_scala.core._ -import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider -import software.amazon.awssdk.regions.Region -import software.amazon.awssdk.services.s3.S3Client -import software.amazon.awssdk.services.s3.model.{CopyObjectRequest, HeadObjectRequest, ListObjectsRequest, ListObjectsV2Request, MetadataDirective, NoSuchKeyException} - -import scala.jdk.CollectionConverters._ -import scala.util.{Failure, Success, Try} - -object EnactS3Changes { - - private val profile = "media-service" - private val region = Region.EU_WEST_1 - private val credentials = DefaultCredentialsProvider.builder.profileName(profile).build - private val s3: S3Client = S3Client.builder - .region(region) - .credentialsProvider(credentials) - .build - - def option(options: List[String], argName: String): (Option[String], List[String]) = { - val arg = options.find(_.startsWith(s"$argName=")) - val value = arg.map(_.stripPrefix(s"$argName=")) - value -> options.filterNot(arg.contains) - } - - def apply(args: List[String]): Unit = { - args match { - case bucketName :: inputFileName :: auditFileName :: options => - val (maybeDrop, optionsAfterDrop) = option(options, "drop") - val (maybeTake, optionsAfterTake) = option(optionsAfterDrop, "take") - val (maybePrefixFilter, unknownOptions) = option(optionsAfterTake, "prefixFilter") - if (unknownOptions.nonEmpty) throw new IllegalArgumentException(s"Didn't recognise options $unknownOptions") - enactS3Changes( - bucketName, - new File(inputFileName), - new File(auditFileName), - maybeDrop.map(_.toInt), - maybeTake.map(_.toInt), - maybePrefixFilter - ) - case _ => throw new IllegalArgumentException("Usage: EnactS3Changes [drop=] [take=] [prefixFilter=]") - } - } - - def getBzipWriter(outputFile: File, append: Boolean = false) = { - val fileOutputStream = new FileOutputStream(outputFile, append) - val compressOutputStream = new BZip2CompressorOutputStream(fileOutputStream) - val sw = new OutputStreamWriter(compressOutputStream) - new BufferedWriter(sw) - } - - case class AuditEntry(status: String, key: String, message: Option[String] = None, details: Option[JsObject] = None) - - object AuditEntry { - def apply(status: String, key: String, message: String): AuditEntry = AuditEntry(status, key, Some(message)) - def apply(status: String, key: String, message: String, details: JsObject): AuditEntry = AuditEntry(status, key, Some(message), Some(details)) - - implicit val writes: OWrites[AuditEntry] = Json.writes[AuditEntry] - def audit(writer: BufferedWriter, entry: AuditEntry): Unit = { - val value = Json.toJsObject(entry) - val json = writeToString[JsValue](value) - writer.append(s"$json\n") - } - val OK = "OK" - val SKIPPED = "SKIPPED" - val ERROR = "ERROR" - } - - def getAuditFileName(auditFile: File, id: Int = 0): File = { - val file = new File(s"${auditFile.getPath}${if (id > 0) s".$id" else ""}") - if (file.exists()) getAuditFileName(auditFile, id+1) else file - } - - def enactS3Changes( - bucketName: String, - inputFile: File, - auditFile: File, - maybeDrop: Option[Int], - maybeTake: Option[Int], - prefixFilter: Option[String]) = { - import AuditEntry._ - - // do this to ensure creds work before starting - s3.listObjectsV2(ListObjectsV2Request.builder.bucket(bucketName).build) - - // reporting - val batchSize = 1000 - val startTime = DateTime.now - var startBatchTime = DateTime.now - var total = 0L - - // auditing - val auditWriter = getBzipWriter(getAuditFileName(auditFile)) - auditWriter.append(s"Enacting changes in $bucketName from $inputFile (dropping: $maybeDrop, taking: $maybeTake, prefixFilter: $prefixFilter)\n") - System.err.println(s"Enacting changes in $bucketName from $inputFile (dropping: $maybeDrop, taking: $maybeTake, prefixFilter: $prefixFilter)") - try { - withSourceFromBzipFile(inputFile) {source => - val lines = source - .getLines() - .map(line => { - val maybeJsValue = Try(readFromString[JsValue](line)).toOption - ( - maybeJsValue.map(_ \ "proposed").flatMap(_.asOpt[ObjectMetadata]), - maybeJsValue.map(_ \ "original").flatMap(_.asOpt[ObjectMetadata]) - ) - }) - val filteredLines = prefixFilter - .map(pf => lines.filter{ - case (Some(proposed), _) => proposed.key.startsWith(pf) - case _ => false - }) - .getOrElse(lines) - val droppedLines = maybeDrop.map(filteredLines.drop).getOrElse(filteredLines) - val linesToProcess = maybeTake.map(droppedLines.take).getOrElse(droppedLines) - - val auditEntries: Iterator[AuditEntry] = linesToProcess - .map { - case (Some(proposed), Some(original)) => - val key = proposed.key - Try {Some(s3.headObject(HeadObjectRequest.builder.bucket(bucketName).key(key).build))}.recover{ - case _:NoSuchKeyException => None - } match { - case Success(Some(headObjectResponse)) => - val check = headObjectResponse.metadata.asScala - if (check!=original.metadata) { - if (check==proposed.metadata) { - AuditEntry(SKIPPED, key, s"Already updated") - } else { - AuditEntry(ERROR, key, s"Metadata doesn't match that expected", Json.obj( - "actual" -> check, - "expected" -> original.metadata - )) - } - } else { - val request = CopyObjectRequest.builder - .sourceBucket(bucketName).sourceKey(key) - .destinationBucket(bucketName).destinationKey(key) - .metadata(proposed.metadata.asJava) - .metadataDirective(MetadataDirective.REPLACE) - .build - Try {s3.copyObject(request)} match { - case Success(_) => AuditEntry(OK, key) - case Failure(e) => AuditEntry(ERROR, key, s"Error whilst copying object ($e)") - } - } - case Success(None) => - AuditEntry(SKIPPED, key, "Object no longer exists") - case Failure(e) => - AuditEntry(ERROR, key, s"Error whilst getting object metadata ($e)") - } - case other => AuditEntry(ERROR, "n/a", s"Unable to parse record", Json.obj("record" -> other.toString)) - } - auditEntries - .map{ entry => - audit(auditWriter, entry) - entry - } - .grouped(batchSize) - .foreach{ auditEntryBatch => - val auditEntries = auditEntryBatch.toList - total = total + auditEntries.size - val now = DateTime.now - val elapsed = new Duration(startTime, now) - val elapsedBatch = new Duration(startBatchTime, now).getMillis - System.err.println(s"${DateTime.now.toString} Processed $total lines") - System.err.println(s"Batch: Processed ${auditEntries.size} in ${elapsedBatch}ms (mean: ${elapsedBatch/auditEntries.size}ms per line)") - System.err.println(s" OK: ${auditEntries.count(_.status==OK)} SKIPPED: ${auditEntries.count(_.status==SKIPPED)} ERROR: ${auditEntries.count(_.status==ERROR)}") - System.err.println(s"Total: Processed $total in ${elapsed.getStandardSeconds}s (mean: ${elapsed.getMillis/total}ms per line)") - startBatchTime = now - } - } - } finally { - auditWriter.close() - } - } - - private def withSourceFromBzipFile[T](file: File)(f: Source => T) = { - val fileInputStream = new FileInputStream(file) - val compressInputStream = new BZip2CompressorInputStream(fileInputStream) - val source = Source.fromInputStream(compressInputStream) - try { - f(source) - } finally { - source.close() - } - } -} diff --git a/scripts/src/main/scala/com/gu/mediaservice/scripts/EsScript.scala b/scripts/src/main/scala/com/gu/mediaservice/scripts/EsScript.scala index 57d1d4ee26..9a172edfd8 100644 --- a/scripts/src/main/scala/com/gu/mediaservice/scripts/EsScript.scala +++ b/scripts/src/main/scala/com/gu/mediaservice/scripts/EsScript.scala @@ -3,196 +3,17 @@ package com.gu.mediaservice.scripts import com.gu.mediaservice.lib.elasticsearch.{ElasticSearchClient, Mappings} import com.sksamuel.elastic4s.ElasticDsl._ import com.sksamuel.elastic4s.handlers.index.GetIndexResponse -import com.sksamuel.elastic4s.requests.bulk.BulkResponse -import com.sksamuel.elastic4s.requests.indexes.IndexRequest -import com.sksamuel.elastic4s.requests.searches.{SearchHit, SearchResponse} +import com.sksamuel.elastic4s.requests.searches.SearchResponse import com.sksamuel.elastic4s.{Indexes, Response} -import org.joda.time.DateTime import play.api.libs.json.Json import java.io.{File, PrintWriter} -import java.util.concurrent.TimeUnit import scala.annotation.tailrec +import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.duration.{Duration, DurationInt, FiniteDuration, SECONDS} -import scala.concurrent.{Await, Future} +import scala.concurrent.duration.{Duration, DurationInt, SECONDS} -object Reindex extends EsScript { - - def run(esUrl: String, args: List[String]) = { - - object IndexClient extends EsClient(esUrl) { - val srcIndexVersionCheck = """images_(\d+)""".r - val srcIndexVersion = this.currentIndex match { - case srcIndexVersionCheck(version) => version.toInt - case _ => 1 - } - val nextIndex = s"${imagesIndexPrefix}_${srcIndexVersion+1}" - } - - def raiseError(msg: String) = { - System.err.println(s"Reindex error on: $esUrl : $msg ") - System.err.println("Exiting...") - IndexClient.client.close() - System.exit(1) - } - - def validateCurrentState(esClient: ElasticSearchClient, from: Option[DateTime]) = { - if(from.exists(_.isAfter(DateTime.now()))) - raiseError("DateTime parameter 'from' must be earlier than the current time" ) - } - - def getArg(argKey: String): Option[String] = { - args.find(_ contains s"${argKey}=") - .map(_ replaceFirst(s"${argKey}=", "")) - } - - val scrollTime = new FiniteDuration(5, TimeUnit.MINUTES) - val scrollSize = 500 - val currentIndex = IndexClient.currentIndex - val newIndex = getArg("NEW_INDEX") match { - case Some(arg) => arg - case None => IndexClient.nextIndex - } - val from = getArg("FROM_TIME") match { - case Some(arg) => Some(DateTime.parse(arg)) - case None => None - } - validateCurrentState(IndexClient, from) - Await.result(reindex(from, IndexClient), Duration.Inf) - println(s"Pointing $esImagesReadAlias to new index: $newIndex") - IndexClient.changeAliasTo(newIndex, currentIndex, esImagesReadAlias) - println(s"Finished reindexing from $currentIndex to $newIndex") - IndexClient.client.close() - - def reindex(from: Option[DateTime], esClient: ElasticSearchClient) : Future[SearchResponse] = { - - def _scroll(scroll: SearchResponse, done: Long = 0): Future[SearchResponse] = { - val client = esClient.client - val currentBatch = done + scrollSize - System.out.println(scrollPercentage(scroll, currentBatch, done)) - - def bulkFromHits(hits: Array[SearchHit]): BulkResponse = { - val bulkRequests: Array[IndexRequest] = hits.map { hit => - indexInto(newIndex) - .withId(hit.id) - .source(hit.sourceAsString) - } - - val bulkResponse = IndexClient.client.execute({ - bulk(bulkRequests) - }).await - - bulkResponse.status match { - case 200 => bulkResponse.result - case _ => { - IndexClient.client.close() - throw new Exception("Failed performing bulk index") - } - } - } - - def scrollPercentage(scroll: SearchResponse, currentBatch: Long, done: Long): String = { - val total = scroll.hits.total.value - // roughly accurate as we're using done, which is relative to scrollSize, rather than the actual number of docs in the new index - val percentage = (Math.min(done,total).toFloat / total) * 100 - s"Reindexing ${Math.min(currentBatch,total)} of $total ($percentage%)" - } - - def performScroll(scrollId: String, scrollTime: FiniteDuration): SearchResponse = { - val scrollResponse = IndexClient.client.execute({ - searchScroll(scrollId) - .keepAlive(scrollTime) - }).await - - scrollResponse.status match { - case 200 => scrollResponse.result - case _ => { - IndexClient.client.close() - throw new Exception("Failed performing bulk index") - } - } - } - - def analyseBulkResponse(bulkResponse: BulkResponse) = { - val successes = bulkResponse.items.filter(item => item.status == 201).map(item => item.id) - val failures = bulkResponse.items.filterNot(item => item.status == 201).map(item => item.id) - println(s"...added ${successes.length}/${bulkResponse.items.length} items in ${bulkResponse.took} ms (${failures.length} failures)") - if (failures.nonEmpty) println(s"......failure IDs: $failures") - } - - val hits = scroll.hits.hits - if(hits.nonEmpty) { - val bulkResponse = bulkFromHits(hits) - analyseBulkResponse(bulkResponse) - val scrollResponse = performScroll(scroll.scrollId.get, scrollTime) - _scroll(scrollResponse, currentBatch) - } else { - println("No more results found") - Future.successful[SearchResponse](scroll) - } - } - - def query(from: Option[DateTime]) : SearchResponse = { - val queryType = from.map(time => - rangeQuery("lastModified").gte(from.get.getMillis).lte(DateTime.now.getMillis) - ).getOrElse( - matchAllQuery() - ) - - val queryResponse = IndexClient.client.execute({ - search(currentIndex) -// .types(Mappings.dummyType) - .scroll(scrollTime) - .size(scrollSize) - .query(queryType) - }).await - - queryResponse.status match { - case 200 => queryResponse.result - case _ => { - IndexClient.client.close() - throw new Exception("Failed performing search query") - } - } - } - - // if no 'from' time parameter is passed, create a new index - if(from.isEmpty) { - IndexClient.createImageIndex(newIndex) - } else { - println(s"Reindexing documents modified since: ${from.toString}") - } - - val startTime = DateTime.now() - println(s"Reindex started at: $startTime") - println(s"Reindexing from: ${IndexClient.currentIndex} to: $newIndex") - val scrollResponse = query(from) - _scroll(scrollResponse) flatMap { case (response: SearchResponse) => - println(s"Pointing ${IndexClient.imagesCurrentAlias} to new index: $newIndex") - IndexClient.changeAliasTo(newIndex, currentIndex) - - val changedDocuments: Long = query(Option(startTime)).hits.total.value - println(s"$changedDocuments changed documents since start") - - if(changedDocuments > 0) { - println(s"Reindexing changes since start time: $startTime") - val recurseResponse = reindex(Option(startTime), esClient) - recurseResponse - } else { - Future.successful(response) - } - } - } - } - - def usageError: Nothing = { - System.err.println("Usage: Reindex error [NEW_INDEX=, FROM_TIME=]") - sys.exit(1) - } -} - // TODO: add the ability to update a section of the mapping object UpdateMapping extends EsScript { diff --git a/scripts/src/main/scala/com/gu/mediaservice/scripts/LoadFromS3Bucket.scala b/scripts/src/main/scala/com/gu/mediaservice/scripts/LoadFromS3Bucket.scala deleted file mode 100644 index 7e7752f8b9..0000000000 --- a/scripts/src/main/scala/com/gu/mediaservice/scripts/LoadFromS3Bucket.scala +++ /dev/null @@ -1,44 +0,0 @@ -package com.gu.mediaservice.scripts - -import com.amazonaws.auth.profile.ProfileCredentialsProvider -import com.amazonaws.auth.{AWSCredentialsProviderChain, InstanceProfileCredentialsProvider} -import com.amazonaws.services.s3.AmazonS3ClientBuilder -import org.apache.http.client.methods.HttpPost -import org.apache.http.entity.{ContentType, InputStreamEntity} -import org.apache.http.impl.client.HttpClients - -import scala.jdk.CollectionConverters._ - -object LoadFromS3Bucket { - - def apply(args: List[String]): Unit = { - - val (bucket, loaderEndpoint) = args match { - case List(b, l) => (b, l) - case _ => sys.error("Usage: LoadFromS3Bucket ") - } - - lazy val awsCredentials = new AWSCredentialsProviderChain( - new ProfileCredentialsProvider("media-service"), - InstanceProfileCredentialsProvider.getInstance() - ) - - val client = AmazonS3ClientBuilder.standard().withCredentials(awsCredentials).build() - - val keys = client.listObjects(bucket).getObjectSummaries.asScala.map(_.getKey) - - val httpClient = HttpClients.createDefault - - for (key <- keys) { - val obj = client.getObject(bucket, key) - val postReq = new HttpPost(loaderEndpoint) - val length = obj.getObjectMetadata.getContentLength - val entity = new InputStreamEntity(obj.getObjectContent, length, ContentType.DEFAULT_BINARY) - postReq.setEntity(entity) - httpClient.execute(postReq).close() - println(s"Loaded image $key") - } - - } - -} diff --git a/scripts/src/main/scala/com/gu/mediaservice/scripts/Main.scala b/scripts/src/main/scala/com/gu/mediaservice/scripts/Main.scala index f7db6dc4d4..e7e3f454c9 100644 --- a/scripts/src/main/scala/com/gu/mediaservice/scripts/Main.scala +++ b/scripts/src/main/scala/com/gu/mediaservice/scripts/Main.scala @@ -4,21 +4,16 @@ package com.gu.mediaservice.scripts object Main extends App { args.toList match { - case "LoadFromS3Bucket" :: as => LoadFromS3Bucket(as) - case "Reindex" :: as => Reindex(as) - case "DownloadAllEsIds" :: as => DownloadAllEsIds(as) case "GetMapping" :: as => GetMapping(as) case "UpdateMapping" :: as => UpdateMapping(as) + case "GetSettings" :: as => GetSettings(as) case "UpdateSettings" :: as => UpdateSettings(as) - case "ConvertConfig" :: as => ConvertConfig(as) - case "BucketMetadata" :: as => BucketMetadata(as) - case "DecodeComparator" :: as => DecodeComparator(as) - case "EnactS3Changes" :: as => EnactS3Changes(as) - case "BulkDeleteS3Files":: as => BulkDeleteS3Files(as) + + case "DownloadAllEsIds" :: as => DownloadAllEsIds(as) case "EsMetadata" :: as => EsImageMetadata(as) - case "ProposeS3Changes" :: as => ProposeS3Changes(as) - case "BackfillEditLastModified" :: as => BackfillEditLastModified(as) + case "BulkDeleteS3Files":: as => BulkDeleteS3Files(as) + case a :: _ => sys.error(s"Unrecognised command: $a") case Nil => sys.error("Usage: ") } diff --git a/scripts/src/main/scala/com/gu/mediaservice/scripts/ProposeS3Changes.scala b/scripts/src/main/scala/com/gu/mediaservice/scripts/ProposeS3Changes.scala deleted file mode 100644 index f6a5a4d8e8..0000000000 --- a/scripts/src/main/scala/com/gu/mediaservice/scripts/ProposeS3Changes.scala +++ /dev/null @@ -1,254 +0,0 @@ -package com.gu.mediaservice.scripts - -import com.gu.mediaservice.JsonDiff -import com.gu.mediaservice.lib.ImageStorageProps -import com.gu.mediaservice.lib.net.URI -import org.apache.commons.compress.compressors.bzip2.{BZip2CompressorInputStream, BZip2CompressorOutputStream} -import org.joda.time.DateTime -import play.api.libs.json.Json - -import java.io._ -import scala.collection.mutable -import scala.io.Source -import scala.language.postfixOps - -object ProposeS3Changes { - def apply(args: List[String]): Unit = { - args match { - case bucketMetadata :: esMetadata :: picdarCsv :: outputFile :: Nil => proposeS3Changes( - new File(bucketMetadata), - new File(esMetadata), - new File(picdarCsv), - new File(s"$outputFile.update.jsonl.bz2"), - new File(s"$outputFile.correct.jsonl.bz2"), - new File(s"$outputFile.esonlykeys.txt"), - new File(s"$outputFile.s3onlykeys.txt"), - new File(s"$outputFile.s3badkeys.txt"), - ) - case _ => throw new IllegalArgumentException("Usage: ProposeS3Changes ") - } - } - - def getBzipWriter(outputFile: File) = { - val fileOutputStream = new FileOutputStream(outputFile) - val compressOutputStream = new BZip2CompressorOutputStream(fileOutputStream) - val sw = new OutputStreamWriter(compressOutputStream) - new BufferedWriter(sw) - } - - def proposeS3Changes( - bucketMetadata: File, - esMetadata: File, - picdarCsv: File, - outputFileForJsonUpdate: File, - outputFileForJsonCorrect: File, - outputFileForESKeys: File, - outputFileForS3Keys: File, - outputFileForBadS3Keys: File) = { - val picdarData = readPicdarCsv(picdarCsv) - System.err.println(s"Completed reading ${picdarData.gridToPicdar.size} Picdar mappings") - - val s3Metadata = { - val (md, badKeys) = readS3Metadata(bucketMetadata) - System.err.println(s"Completed reading S3 metadata. ${md.size} records, ${badKeys.size} bad keys") - // write out the bad keys in this scope so they can then be GCd - val outputWriterForBadS3Keys = new FileWriter(outputFileForBadS3Keys) - try { - badKeys.foreach(k => outputWriterForBadS3Keys.append(s"$k\n")) - } finally { - outputWriterForBadS3Keys.close() - } - md - } - - var s3KeysNotYetSeenInEs = s3Metadata.keySet - var esKeysNotInS3 = Set.empty[String] - - System.err.println(s"Starting change proposals...") - val outputWriterForJsonUpdate = getBzipWriter(outputFileForJsonUpdate) - val outputWriterForJsonCorrect = getBzipWriter(outputFileForJsonCorrect) - val outputWriterForESKeys = new FileWriter(outputFileForESKeys) - val outputWriterForS3Keys = new FileWriter(outputFileForS3Keys) - try { - withSourceFromBzipFile(esMetadata){ source => - source - .getLines() - .flatMap(line => Json.fromJson[EsDocumentWithMetadata](Json.parse(line)).asOpt) - .zipWithIndex - .foreach { case (metadata, i) => - if (i % 10000 == 0) System.err.println(s"Processing ES metadata line $i") - val id = metadata.id - val maybeS3 = s3Metadata.get(id) - maybeS3 match { - case Some(s3Metadata) => - s3KeysNotYetSeenInEs -= id - val mergedMetadata = mergeMetadata(metadata, s3Metadata, picdarData) - if (mergedMetadata != mergeMetadata(metadata, mergedMetadata, picdarData)) { - System.err.println(s"Merged metadata for $id not idempotent") - } - if (mergedMetadata != s3Metadata) { - val jsS3 = Json.toJson(s3Metadata) - val jsMerged = Json.toJson(mergedMetadata) - val diff = JsonDiff.diff(jsS3, jsMerged) - outputWriterForJsonUpdate - .append(s"${Json.toJson(Json.obj( - "original" -> jsS3, - "proposed" -> jsMerged, - "diff" -> diff - )).toString()}\n") - } else { - outputWriterForJsonCorrect - .append(s"${Json.toJson(mergedMetadata).toString()}\n") - } - case None => - esKeysNotInS3 += id - } - } - } - - esKeysNotInS3.foreach(k => outputWriterForESKeys.append(s"$k\n")) - s3KeysNotYetSeenInEs.foreach(k => outputWriterForS3Keys.append(s"$k\n")) - } finally { - outputWriterForJsonUpdate.close() - outputWriterForJsonCorrect.close() - outputWriterForESKeys.close() - outputWriterForS3Keys.close() - } - } - - - def mergeMetadata(esMetadata: EsDocumentWithMetadata, s3Metadata: ObjectMetadata, picdarData: PicdarData): ObjectMetadata = { - /* If we can we should retain the legacy keys with _ in so that we don't have to touch the object */ - def bestKeyNameFor(dashVariant: String): String = { - val hasDashVariant = s3Metadata.metadata.contains(dashVariant) - // does this have underscore version of key? - val underscoreVariant = dashVariant.replace("-", "_") - val hasUnderscoreVariant = s3Metadata.metadata.contains(underscoreVariant) - if (hasDashVariant && hasUnderscoreVariant) { - System.err.println(s"Warning: both dash and underscore keys on ${s3Metadata.key}") - } - if (hasUnderscoreVariant) - underscoreVariant - else - dashVariant - } - - /* Find the "best" value, trying hard to detect values that haven't really changed despite encoding or format changes */ - def bestValue(maybeEsValue: Option[String], maybeS3Value: Option[String], isDate: Boolean = false): Option[String] = { - def isSame(esValue: String, s3Value: String): Boolean = { - val decodedS3 = URI.decode(s3Value) - if (!isDate) { - decodedS3 == esValue - } else { - val s3Date = DateTime.parse(decodedS3) - val esDate = DateTime.parse(esValue) - s3Date.equals(esDate) - } - } - (maybeEsValue, maybeS3Value) match { - case (None, s3Value) => s3Value - case (Some(esValue), Some(s3Value)) if isSame(esValue, s3Value) => Some(s3Value) - case (Some(esValue), _) => Some(URI.encode(esValue)) - } - } - - val filenameKey = bestKeyNameFor(ImageStorageProps.filenameMetadataKey) - val uploadedByKey = bestKeyNameFor(ImageStorageProps.uploadedByMetadataKey) - val uploadTimeKey = bestKeyNameFor(ImageStorageProps.uploadTimeMetadataKey) - - if (metadataEquivalent(esMetadata, s3Metadata)) { - s3Metadata - } else { - // filename: taken from ES if it exists, then from S3, otherwise empty - val fileName = bestValue(esMetadata.fileName, s3Metadata.metadata.get(filenameKey)) - .map(s => s.replaceAll(s" (${esMetadata.id})", "")) - // uploaded by: taken from ES if it exists, then from S3, otherwise empty - val uploadedBy = bestValue(esMetadata.uploadedBy, s3Metadata.metadata.get(uploadedByKey)) - // uploaded time: taken from ES if it exists, then from S3, otherwise empty - val uploadTime = bestValue(esMetadata.uploadTime, s3Metadata.metadata.get(uploadTimeKey), isDate = true) - - // Find ALL identifiers in elasticsearch (put "identifier!" on the front and make lowercase) - val esIdentifiers = esMetadata.identifiers - .map{ case (key, value ) => s"${ImageStorageProps.identifierMetadataKeyPrefix}$key".toLowerCase -> URI.encode(value)} - - // Find all OUR identifiers in S3 (must have "identifier!" on the front) - val s3Identifiers = s3Metadata.metadata - .filter{case (key, _) => key.startsWith(ImageStorageProps.identifierMetadataKeyPrefix)} - - val picdarIdEntry = picdarData.gridToPicdar.get(esMetadata.id).map(s"${ImageStorageProps.identifierMetadataKeyPrefix}picdarurn" ->) - - // Merge the two maps together with any picdar entry - val allIdentifierKeys = s3Identifiers.keySet ++ esIdentifiers.keySet - val identifiers = allIdentifierKeys.foldLeft(Map.empty[String, String]) { case (acc, key) => - acc ++ bestValue(esIdentifiers.get(key), s3Identifiers.get(key)).map(key ->) - } ++ picdarIdEntry - - ObjectMetadata( - key = s3Metadata.key, - lastModified = s3Metadata.lastModified, - metadata = identifiers - ++ fileName.map(fn => filenameKey -> fn) - ++ uploadedBy.map(ub => uploadedByKey -> ub) - ++ uploadTime.map(ut => uploadTimeKey -> ut) - ) - } - } - - def metadataEquivalent(metadata: EsDocumentWithMetadata, s3Metadata: ObjectMetadata): Boolean = { - metadata.uploadTime == s3Metadata.metadata.get(ImageStorageProps.uploadTimeMetadataKey) && - metadata.uploadedBy == s3Metadata.metadata.get(ImageStorageProps.uploadedByMetadataKey) && - metadata.fileName == s3Metadata.metadata.get(ImageStorageProps.filenameMetadataKey) && - metadata.identifiers.map{case (key, value) => - ImageStorageProps.identifierMetadataKeyPrefix + key.toLowerCase -> value - } == s3Metadata.metadata.filter{ case (key, _) => key.startsWith(ImageStorageProps.identifierMetadataKeyPrefix)} - } - - private val hex = """[0-9a-f]""" - private val GoodKey = s"""^($hex)/($hex)/($hex)/($hex)/($hex)/($hex)/(\\1\\2\\3\\4\\5\\6$hex{34})$$""".r - def readS3Metadata(bucketMetadata: File): (Map[String, ObjectMetadata], List[String]) = { - val (goodEntries, badValues) = withSourceFromBzipFile(bucketMetadata){ source => - source.getLines().zipWithIndex.foldLeft[(mutable.Builder[(String, ObjectMetadata), Map[String, ObjectMetadata]], List[String])] (Map.newBuilder[String, ObjectMetadata], Nil) { case ((goodEntries, badKeys), (line, i)) => - if (i % 10000 == 0) System.err.println(s"Loading index $i") - Json.fromJson[ObjectMetadata](Json.parse(line)).asOpt match { - case Some(objMd@ObjectMetadata(GoodKey(_, _, _, _, _, _, id), _, _)) => (goodEntries += (id -> objMd), badKeys) - case Some(ObjectMetadata(badKey, _, _)) => (goodEntries, badKey :: badKeys) - case None => (goodEntries, badKeys) - } - } - } - (goodEntries.result(), badValues) - } - - case class PicdarData(gridToPicdar: Map[String, String]) - - def readPicdarCsv(picdarCsvFile: File): PicdarData = { - val source = Source.fromFile(picdarCsvFile) - try { - val gridIdAndPicdarId = source.getLines().collect { - case line if line.contains(",") => - line.takeWhile(_ != ',') -> line.dropWhile(_ != ',').drop(1) - }.toList - val gridIds = gridIdAndPicdarId.map{ case (gridId, _) => gridId } - val picdarIds = gridIdAndPicdarId.map{ case (_, picdarId) => picdarId } - if (gridIds.length != gridIds.toSet.size) { - throw new IllegalArgumentException(s"Picdar CSV file contains duplicate IDs: ${gridIds.length} / ${gridIds.toSet.size} uniques") - } - val gridToPicdar = gridIdAndPicdarId.toMap - //val picdarToGrid = gridIdAndPicdarId.map(_.swap).toMap - PicdarData(gridToPicdar) - } finally { - source.close() - } - } - - private def withSourceFromBzipFile[T](file: File)(f: Source => T) = { - val fileInputStream = new FileInputStream(file) - val compressInputStream = new BZip2CompressorInputStream(fileInputStream) - val source = Source.fromInputStream(compressInputStream) - try { - f(source) - } finally { - source.close() - } - } -}