hocon-1.3.1/ 0000755 0000041 0000041 00000000000 13704151031 012645 5 ustar www-data www-data hocon-1.3.1/README.md 0000644 0000041 0000041 00000014043 13704151031 014126 0 ustar www-data www-data ruby-hocon
==========
[](https://badge.fury.io/rb/hocon) [](https://travis-ci.org/puppetlabs/ruby-hocon)
This is a port of the [Typesafe Config](https://github.com/typesafehub/config) library to Ruby.
The library provides Ruby support for the [HOCON](https://github.com/typesafehub/config/blob/master/HOCON.md) configuration file format.
At present, it supports parsing and modification of existing HOCON/JSON files via the `ConfigFactory`
class and the `ConfigValueFactory` class, and rendering parsed config objects back to a String
([see examples below](#basic-usage)). It also supports the parsing and modification of HOCON/JSON files via
`ConfigDocumentFactory`.
**Note:** While the project is production ready, since not all features in the Typesafe library are supported,
you may still run into some issues. If you find a problem, feel free to open a github issue.
The implementation is intended to be as close to a line-for-line port as the two languages allow,
in hopes of making it fairly easy to port over new changesets from the Java code base over time.
Support
=======
For best results, if you find an issue with this library, please open an issue on our [Jira issue tracker](https://tickets.puppetlabs.com/browse/HC). Issues filed there tend to be more visible to the current maintainers than issues on the Github issue tracker.
Basic Usage
===========
```sh
gem install hocon
```
To use the simple API, for reading config values:
```rb
require 'hocon'
conf = Hocon.load("myapp.conf")
puts "Here's a setting: #{conf["foo"]["bar"]["baz"]}"
```
By default, the simple API will determine the configuration file syntax/format
based on the filename extension of the file; `.conf` will be interpreted as HOCON,
`.json` will be interpreted as strict JSON, and any other extension will cause an
error to be raised since the syntax is unknown. If you'd like to use a different
file extension, you manually specify the syntax, like this:
```rb
require 'hocon'
require 'hocon/config_syntax'
conf = Hocon.load("myapp.blah", {:syntax => Hocon::ConfigSyntax::HOCON})
```
Supported values for `:syntax` are: JSON, CONF, and HOCON. (CONF and HOCON are
aliases, and both map to the underlying HOCON syntax.)
To use the ConfigDocument API, if you need both read/write capability for
modifying settings in a config file, or if you want to retain access to
things like comments and line numbers:
```rb
require 'hocon/parser/config_document_factory'
require 'hocon/config_value_factory'
# The below 4 variables will all be ConfigDocument instances
doc = Hocon::Parser::ConfigDocumentFactory.parse_file("myapp.conf")
doc2 = doc.set_value("a.b", "[1, 2, 3, 4, 5]")
doc3 = doc.remove_value("a")
doc4 = doc.set_config_value("a.b", Hocon::ConfigValueFactory.from_any_ref([1, 2, 3, 4, 5]))
doc_has_value = doc.has_value?("a") # returns boolean
orig_doc_text = doc.render # returns string
```
Note that a `ConfigDocument` is used primarily for simple configuration manipulation while preserving
whitespace and comments. As such, it is not powerful as the regular `Config` API, and will not resolve
substitutions.
CLI Tool
========
The `hocon` gem comes bundles with a `hocon` command line tool which can be used to get and set values from hocon files
```
Usage: hocon [options] {get,set,unset} PATH [VALUE]
Example usages:
hocon -i settings.conf -o new_settings.conf set some.nested.value 42
hocon -f settings.conf set some.nested.value 42
cat settings.conf | hocon get some.nested.value
Subcommands:
get PATH - Returns the value at the given path
set PATH VALUE - Sets or adds the given value at the given path
unset PATH - Removes the value at the given path
Options:
-i, --in-file HOCON_FILE HOCON file to read/modify. If omitted, STDIN assumed
-o, --out-file HOCON_FILE File to be written to. If omitted, STDOUT assumed
-f, --file HOCON_FILE File to read/write to. Equivalent to setting -i/-o to the same file
-j, --json Output values from the 'get' subcommand in json format
-h, --help Show this message
-v, --version Show version
```
CLI Examples
--------
### Basic Usage
```
$ cat settings.conf
{
foo: bar
}
$ hocon -i settings.conf get foo
bar
$ hocon -i settings.conf set foo baz
$ cat settings.conf
{
foo: baz
}
# Write to a different file
$ hocon -i settings.conf -o new_settings.conf set some.nested.value 42
$ cat new_settings.conf
{
foo: bar
some: {
nested: {
value: 42
}
}
}
# Write back to the same file
$ hocon -f settings.conf set some.nested.value 42
$ cat settings.conf
{
foo: bar
some: {
nested: {
value: 42
}
}
}
```
### Complex Values
If you give `set` a properly formatted hocon dictionary or array, it will try to accept it
```
$ hocon -i settings.conf set foo "{one: [1, 2, 3], two: {hello: world}}"
{
foo: {one: [1, 2, 3], two: {hello: world}}
}
```
### Chaining
If `--in-file` or `--out-file` aren't specified, STDIN and STDOUT are used for the missing options. Therefore it's possible to chain `hocon` calls
```
$ cat settings.conf
{
foo: bar
}
$ cat settings.conf | hocon set foo 42 | hocon set one.two three
{
foo: 42
one: {
two: three
}
}
```
### JSON Output
Calls to the `get` subcommand will return the data in HOCON format by default, but setting the `-j/--json` flag will cause it to return a valid JSON object
```
$ cat settings.conf
foo: {
bar: {
baz: 42
}
}
$ hocon -i settings.conf get foo --json
{
"bar": {
"baz": 42
}
}
```
Testing
=======
```sh
bundle install --path .bundle
bundle exec rspec spec
```
Unsupported Features
====================
This supports many of the same things as the Java library, but there are some notable exceptions.
Unsupported features include:
* Non file includes
* Loading resources from the class path or URLs
* Properties files
* Parsing anything other than files and strings
* Duration and size settings
* Java system properties
hocon-1.3.1/bin/ 0000755 0000041 0000041 00000000000 13704151031 013415 5 ustar www-data www-data hocon-1.3.1/bin/hocon 0000755 0000041 0000041 00000000127 13704151031 014451 0 ustar www-data www-data #!/usr/bin/env ruby
require 'hocon/cli'
Hocon::CLI.main(Hocon::CLI.parse_args(ARGV))
hocon-1.3.1/CHANGELOG.md 0000644 0000041 0000041 00000007015 13704151031 014461 0 ustar www-data www-data ## 1.3.1
This is a bugfix release
* Fix a bug when using the library in multiple threads ([HC-105](https://tickets.puppetlabs.com/browse/HC-105))
## 1.3.0
This is a feature release
* Support environment variable lists ([HC-104](https://tickets.puppetlabs.com/browse/HC-104))
## 1.2.6
This is a bugfix release
* Do not ship spec folder with gem ([PA-2942](https://tickets.puppetlabs.com/browse/PA-2942))
## 1.2.5
This is a bugfix release
* Fixed loading files with UTF-8 characters in their file paths
## 1.2.4
This is a feature release.
* Added a cli tool called `hocon` for reading and manipulating hocon files
Note that the version numbers 1.2.0-1.2.3 were not used because of bugs in our
release pipeline we were working out
## 1.1.3
This is a bugfix release.
* Fixed bug where Hocon.parse would throw a ConfigNotResolved error if you passed it a String
that contained values with substitutions.
## 1.1.2
This is a bugfix release.
* Fixed bug where Hocon::ConfigFactory.parse_file was not handling files with BOMs on Windows,
causing UTF-8 files to not load properly.
## 1.1.1
This is a bugfix release.
* Fixed a bug where an undefined method `value_type_name` error was being thrown due to
improper calls to the class method.
## 1.1.0
This is a bugfix/feature release
* Fixed a bug where unrecognized config file extensions caused `Hocon.load` to return an empty
hash instead of an error.
* Added an optional `:syntax` key to the `Hocon.load` method to explicitly specify the file format
* Renamed internal usage of `name` methods to avoid overriding built in `Object#name` method
## 1.0.1
This is a bugfix release.
The API is stable enough and the code is being used in production, so the version is also being bumped to 1.0.0
* Fixed a bug wherein calling "Hocon.load" would not
resolve substitutions.
* Fixed a circular dependency between the Hocon and Hocon::ConfigFactory
namespaces. Using the Hocon::ConfigFactory class now requires you to
use a `require 'hocon/config_factory'` instead of `require hocon`
* Add support for hashes with keyword keys
## 1.0.0
This version number was burned.
## 0.9.3
This is a bugfix release.
* Fixed a bug wherein inserting an array or a hash into a ConfigDocument would cause
"# hardcoded value" comments to be generated before every entry in the hash/array.
## 0.9.2
This is a bugfix release
* Fixed a bug wherein attempting to insert a complex value (such as an array or a hash) into an empty
ConfigDocument would cause an undefined method error.
## 0.9.1
This is a bugfix release.
* Fixed a bug wherein ugly configurations were being generated due to the addition of new objects when a setting
is set at a path that does not currently exist in the configuration. Previously, these new objects were being
added as single-line objects. They will now be added as multi-line objects if the parent object is a multi-line
object or is an empty root object.
## 0.9.0
This is a promotion of the 0.1.0 release with one small bug fix:
* Fixed bug wherein using the `set_config_value` method with some parsed values would cause a failure due to surrounding whitespace
## 0.1.0
This is a feature release containing a large number of changes and improvements
* Added support for concatenation
* Added support for substitutions
* Added support for file includes. Other types of includes are not supported
* Added the new ConfigDocument API that was recently implemented in the upstream Java library
* Improved JSON support
* Fixed a large number of small bugs related to various pieces of implementation
hocon-1.3.1/LICENSE 0000644 0000041 0000041 00000026075 13704151031 013664 0 ustar www-data www-data Apache License
Version 2.0, January 2004
http://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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
hocon-1.3.1/lib/ 0000755 0000041 0000041 00000000000 13704151031 013413 5 ustar www-data www-data hocon-1.3.1/lib/hocon/ 0000755 0000041 0000041 00000000000 13704151031 014521 5 ustar www-data www-data hocon-1.3.1/lib/hocon/config_value_type.rb 0000644 0000041 0000041 00000001230 13704151031 020544 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_error'
#
# The type of a configuration value (following the JSON type schema).
#
module Hocon::ConfigValueType
OBJECT = 0
LIST = 1
NUMBER = 2
BOOLEAN = 3
NULL = 4
STRING = 5
def self.value_type_name(config_value_type)
case config_value_type
when OBJECT then "OBJECT"
when LIST then "LIST"
when NUMBER then "NUMBER"
when BOOLEAN then "BOOLEAN"
when NULL then "NULL"
when STRING then "STRING"
else raise Hocon::ConfigError::ConfigBugOrBrokenError, "Unrecognized value type '#{config_value_type}'"
end
end
end
hocon-1.3.1/lib/hocon/parser/ 0000755 0000041 0000041 00000000000 13704151031 016015 5 ustar www-data www-data hocon-1.3.1/lib/hocon/parser/config_node.rb 0000644 0000041 0000041 00000002000 13704151031 020604 0 ustar www-data www-data # encoding: utf-8
require 'hocon/parser'
require 'hocon/config_error'
#
# An immutable node that makes up the ConfigDocument AST, and which can be
# used to reproduce part or all of the original text of an 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 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.
#
module Hocon::Parser::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
#
def render
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigNode should override `render` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/parser/config_document_factory.rb 0000644 0000041 0000041 00000001767 13704151031 023247 0 ustar www-data www-data # encoding: utf-8
require 'hocon/parser'
require 'hocon/impl/parseable'
require 'hocon/config_parse_options'
#
# Factory for creating {@link
# com.typesafe.config.parser.ConfigDocument} instances.
#
class Hocon::Parser::ConfigDocumentFactory
#
# 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 com.typesafe.config.ConfigException on IO or parse errors
#
def self.parse_file(file, options = Hocon::ConfigParseOptions.defaults)
Hocon::Impl::Parseable.new_file(file, options).parse_config_document
end
#
# Parses a string which should be valid HOCON or JSON.
#
# @param s string to parse
# @param options parse options
# @return the parsed configuration
#
def self.parse_string(s, options = Hocon::ConfigParseOptions.defaults)
Hocon::Impl::Parseable.new_string(s, options).parse_config_document
end
end hocon-1.3.1/lib/hocon/parser/config_document.rb 0000644 0000041 0000041 00000010216 13704151031 021505 0 ustar www-data www-data # encoding: utf-8
require 'hocon/parser'
require 'hocon/config_error'
#
# 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.
#
module Hocon::Parser::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
#
def set_value(path, new_value)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigDocument should override `render` (#{self.class})"
end
#
# Returns a new ConfigDocument that is a copy of the current ConfigDocument,
# but with the desired value set at the desired path as with {@link #setValue(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
#
def set_config_value(path, new_value)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigDocument should override `render` (#{self.class})"
end
#
# Returns a new ConfigDocument that is a copy of the current ConfigDocument, but with
# the value at the desired path removed. If the desired 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.
#
def remove_value(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigDocument should override `render` (#{self.class})"
end
#
# Returns a boolean indicating whether or not a ConfigDocument has a value at the desired path.
# @param path the path to check
# @return true if the path exists in the document, otherwise false
#
def has_value?(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigDocument should override `render` (#{self.class})"
end
#
# The original text of the input, modified if necessary with
# any replaced or added values.
# @return the modified original text
#
def render
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigDocument should override `render` (#{self.class})"
end
end hocon-1.3.1/lib/hocon/impl/ 0000755 0000041 0000041 00000000000 13704151031 015462 5 ustar www-data www-data hocon-1.3.1/lib/hocon/impl/path_builder.rb 0000644 0000041 0000041 00000001762 13704151031 020457 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/path'
require 'hocon/config_error'
class Hocon::Impl::PathBuilder
def initialize
@keys = []
@result = nil
end
def check_can_append
if @result
raise Hocon::ConfigError::ConfigBugOrBrokenError, "Adding to PathBuilder after getting result"
end
end
def append_key(key)
check_can_append
@keys.push(key)
end
def append_path(path)
check_can_append
first = path.first
remainder = path.remainder
loop do
@keys.push(first)
if !remainder.nil?
first = remainder.first
remainder = remainder.remainder
else
break
end
end
end
def result
# note: if keys is empty, we want to return nil, which is a valid
# empty path
if @result.nil?
remainder = nil
while !@keys.empty?
key = @keys.pop
remainder = Hocon::Impl::Path.new(key, remainder)
end
@result = remainder
end
@result
end
end
hocon-1.3.1/lib/hocon/impl/config_node_path.rb 0000644 0000041 0000041 00000002154 13704151031 021277 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/tokens'
require 'hocon/impl/abstract_config_node'
class Hocon::Impl::ConfigNodePath
include Hocon::Impl::AbstractConfigNode
Tokens = Hocon::Impl::Tokens
def initialize(path, tokens)
@path = path
@tokens = tokens
end
attr_reader :tokens
def value
@path
end
def sub_path(to_remove)
period_count = 0
tokens_copy = tokens.clone
(0..tokens_copy.size - 1).each do |i|
if Tokens.unquoted_text?(tokens_copy[i]) &&
tokens_copy[i].token_text == "."
period_count += 1
end
if period_count == to_remove
return self.class.new(@path.sub_path_to_end(to_remove), tokens_copy[i + 1..tokens_copy.size])
end
end
raise ConfigBugOrBrokenError, "Tried to remove too many elements from a Path node"
end
def first
tokens_copy = tokens.clone
(0..tokens_copy.size - 1).each do |i|
if Tokens.unquoted_text?(tokens_copy[i]) &&
tokens_copy[i].token_text == "."
return self.class.new(@path.sub_path(0, 1), tokens_copy[0, i])
end
end
self
end
end
hocon-1.3.1/lib/hocon/impl/simple_config_document.rb 0000644 0000041 0000041 00000002606 13704151031 022527 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/parser/config_document'
require 'hocon/impl/config_document_parser'
require 'hocon/config_render_options'
class Hocon::Impl::SimpleConfigDocument
include Hocon::Parser::ConfigDocument
def initialize(parsed_node, parse_options)
@config_node_tree = parsed_node
@parse_options = parse_options
end
def set_value(path, new_value)
origin = Hocon::Impl::SimpleConfigOrigin.new_simple("single value parsing")
reader = StringIO.new(new_value)
tokens = Hocon::Impl::Tokenizer.tokenize(origin, reader, @parse_options.syntax)
parsed_value = Hocon::Impl::ConfigDocumentParser.parse_value(tokens, origin, @parse_options)
reader.close
self.class.new(@config_node_tree.set_value(path, parsed_value, @parse_options.syntax), @parse_options)
end
def set_config_value(path, new_value)
options = Hocon::ConfigRenderOptions.defaults
options.origin_comments = false
set_value(path, new_value.render(options).strip)
end
def remove_value(path)
self.class.new(@config_node_tree.set_value(path, nil, @parse_options.syntax), @parse_options)
end
def has_value?(path)
@config_node_tree.has_value(path)
end
def render
@config_node_tree.render
end
def ==(other)
other.class.ancestors.include?(Hocon::Parser::ConfigDocument) && render == other.render
end
def hash
render.hash
end
end
hocon-1.3.1/lib/hocon/impl/origin_type.rb 0000644 0000041 0000041 00000000670 13704151031 020342 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
module Hocon::Impl::OriginType
## for now, we only support a subset of these
GENERIC = 0
FILE = 1
#URL = 2
# We don't actually support loading from the classpath / loadpath, which is
# what 'RESOURCE' is about in the upstream library. However, some code paths
# still flow through our simplistic implementation of `ParseableResource`, so
# we need this constant.
RESOURCE = 3
end
hocon-1.3.1/lib/hocon/impl/simple_config_object.rb 0000644 0000041 0000041 00000035154 13704151031 022163 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/simple_config_origin'
require 'hocon/impl/abstract_config_object'
require 'hocon/impl/resolve_status'
require 'hocon/impl/resolve_result'
require 'hocon/impl/path'
require 'hocon/config_error'
require 'set'
require 'forwardable'
class Hocon::Impl::SimpleConfigObject
include Hocon::Impl::AbstractConfigObject
extend Forwardable
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
ResolveStatus = Hocon::Impl::ResolveStatus
ResolveResult = Hocon::Impl::ResolveResult
SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin
Path = Hocon::Impl::Path
def initialize(origin,
value,
status = Hocon::Impl::ResolveStatus.from_values(value.values),
ignores_fallbacks = false)
super(origin)
if value.nil?
raise ConfigBugOrBrokenError, "creating config object with null map"
end
@value = value
@resolved = (status == Hocon::Impl::ResolveStatus::RESOLVED)
@ignores_fallbacks = ignores_fallbacks
# Kind of an expensive debug check. Comment out?
if status != Hocon::Impl::ResolveStatus.from_values(value.values)
raise ConfigBugOrBrokenError, "Wrong resolved status on #{self}"
end
end
attr_reader :value
# To support accessing ConfigObjects like a hash
def_delegators :@value, :[], :has_key?, :has_value?, :empty?, :size, :keys, :values, :each, :map
def with_only_key(key)
with_only_path(Path.new_key(key))
end
def without_key(key)
without_path(Path.new_key(key))
end
# 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.
def with_only_path_or_nil(path)
key = path.first
path_next = path.remainder
v = value[key]
if ! path_next.nil?
if (!v.nil?) && (v.is_a?(Hocon::Impl::AbstractConfigObject))
v = v.with_only_path_or_nil(path_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 = nil
end
end
if v.nil?
nil
else
self.class.new(origin, {key => v}, v.resolve_status, @ignores_fallbacks)
end
end
def with_only_path(path)
o = with_only_path_or_nil(path)
if o.nil?
self.class.new(origin, {}, ResolveStatus::RESOLVED, @ignores_fallbacks)
else
o
end
end
def without_path(path)
key = path.first
remainder = path.remainder
v = @value[key]
if (not v.nil?) && (not remainder.nil?) && v.is_a?(Hocon::Impl::AbstractConfigObject)
v = v.without_path(remainder)
updated = @value.clone
updated[key] = v
self.class.new(origin,
updated,
ResolveStatus.from_values(updated.values), @ignores_fallbacks)
elsif (not remainder.nil?) || v.nil?
return self
else
smaller = Hash.new
@value.each do |old_key, old_value|
unless old_key == key
smaller[old_key] = old_value
end
end
self.class.new(origin,
smaller,
ResolveStatus.from_values(smaller.values), @ignores_fallbacks)
end
end
def with_value(path, v)
key = path.first
remainder = path.remainder
if remainder.nil?
with_key_value(key, v)
else
child = @value[key]
if (not child.nil?) && child.is_a?(Hocon::Impl::AbstractConfigObject)
return with_key_value(key, child.with_value(remainder, v))
else
subtree = v.at_path_with_origin(
SimpleConfigOrigin.new_simple("with_value(#{remainder.render})"), remainder)
with_key_value(key, subtree.root)
end
end
end
def with_key_value(key, v)
if v.nil?
raise ConfigBugOrBrokenError.new("Trying to store null ConfigValue in a ConfigObject")
end
new_map = Hash.new
if @value.empty?
new_map[key] = v
else
new_map = @value.clone
new_map[key] = v
end
self.class.new(origin, new_map, ResolveStatus.from_values(new_map.values), @ignores_fallbacks)
end
def attempt_peek_with_partial_resolve(key)
@value[key]
end
def new_copy_with_status(new_status, new_origin, new_ignores_fallbacks = nil)
self.class.new(new_origin, @value, new_status, new_ignores_fallbacks)
end
def with_fallbacks_ignored()
if @ignores_fallbacks
self
else
new_copy_with_status(resolve_status, origin, true)
end
end
def resolve_status
ResolveStatus.from_boolean(@resolved)
end
def replace_child(child, replacement)
new_children = @value.clone
new_children.each do |old, old_value|
if old_value.equal?(child)
if replacement != nil
new_children[old] = replacement
else
new_children.delete(old)
end
return self.class.new(origin, new_children, ResolveStatus.from_values(new_children.values),
@ignores_fallbacks)
end
end
raise ConfigBugOrBrokenError, "SimpleConfigObject.replaceChild did not find #{child} in #{self}"
end
def has_descendant?(descendant)
value.values.each do |child|
if child.equal?(descendant)
return true
end
end
# now do the expensive search
value.values.each do |child|
if child.is_a?(Hocon::Impl::Container) && child.has_descendant?(descendant)
return true
end
end
false
end
def ignores_fallbacks?
@ignores_fallbacks
end
def unwrapped
m = {}
@value.each do |k,v|
m[k] = v.unwrapped
end
m
end
def merged_with_object(abstract_fallback)
require_not_ignoring_fallbacks
unless abstract_fallback.is_a?(Hocon::Impl::SimpleConfigObject)
raise ConfigBugOrBrokenError, "should not be reached (merging non-SimpleConfigObject)"
end
fallback = abstract_fallback
changed = false
all_resolved = true
merged = {}
all_keys = key_set.union(fallback.key_set)
all_keys.each do |key|
first = @value[key]
second = fallback.value[key]
kept =
if first.nil?
second
elsif second.nil?
first
else
first.with_fallback(second)
end
merged[key] = kept
if first != kept
changed = true
end
if kept.resolve_status == Hocon::Impl::ResolveStatus::UNRESOLVED
all_resolved = false
end
end
new_resolve_status = Hocon::Impl::ResolveStatus.from_boolean(all_resolved)
new_ignores_fallbacks = fallback.ignores_fallbacks?
if changed
Hocon::Impl::SimpleConfigObject.new(Hocon::Impl::AbstractConfigObject.merge_origins([self, fallback]),
merged, new_resolve_status,
new_ignores_fallbacks)
elsif (new_resolve_status != resolve_status) || (new_ignores_fallbacks != ignores_fallbacks?)
new_copy_with_status(new_resolve_status, origin, new_ignores_fallbacks)
else
self
end
end
def modify(modifier)
begin
modify_may_throw(modifier)
rescue Hocon::ConfigError => e
raise e
end
end
def modify_may_throw(modifier)
changes = nil
keys.each do |k|
v = value[k]
# "modified" may be null, which means remove the child;
# to do that we put null in the "changes" map.
modified = modifier.modify_child_may_throw(k, v)
if ! modified.equal?(v)
if changes.nil?
changes = {}
end
changes[k] = modified
end
end
if changes.nil?
self
else
modified = {}
saw_unresolved = false
keys.each do |k|
if changes.has_key?(k)
new_value = changes[k]
if ! new_value.nil?
modified[k] = new_value
if new_value.resolve_status == ResolveStatus::UNRESOLVED
saw_unresolved = true
end
else
# remove this child; don't put it in the new map
end
else
new_value = value[k]
modified[k] = new_value
if new_value.resolve_status == ResolveStatus::UNRESOLVED
saw_unresolved = true
end
end
end
self.class.new(origin, modified,
saw_unresolved ? ResolveStatus::UNRESOLVED : ResolveStatus::RESOLVED,
@ignores_fallbacks)
end
end
class ResolveModifier
attr_accessor :context
attr_reader :source
def initialize(context, source)
@context = context
@source = source
@original_restrict = context.restrict_to_child
end
def modify_child_may_throw(key, v)
if @context.is_restricted_to_child
if key == @context.restrict_to_child.first
remainder = @context.restrict_to_child.remainder
if remainder != nil
result = @context.restrict(remainder).resolve(v, @source)
@context = result.context.unrestricted.restrict(@original_restrict)
return result.value
else
# we don't want to resolve the leaf child
return v
end
else
# not in the restrictToChild path
return v
end
else
# no restrictToChild, resolve everything
result = @context.unrestricted.resolve(v, @source)
@context = result.context.unrestricted.restrict(@original_restrict)
result.value
end
end
end
def resolve_substitutions(context, source)
if resolve_status == ResolveStatus::RESOLVED
return ResolveResult.make(context, self)
end
source_with_parent = source.push_parent(self)
begin
modifier = ResolveModifier.new(context, source_with_parent)
value = modify_may_throw(modifier)
ResolveResult.make(modifier.context, value)
rescue NotPossibleToResolve => e
raise e
rescue Hocon::ConfigError => e
raise e
end
end
def relativized(prefix)
modifier = Class.new do
include Hocon::Impl::AbstractConfigValue::NoExceptionsModifier
# prefix isn't in scope inside of a def, but it is in scope inside of Class.new
# so manually define a method that has access to prefix
# I feel dirty
define_method(:modify_child) do |key, v|
v.relativized(prefix)
end
end
modify(modifier.new)
end
class RenderComparator
def self.all_digits?(s)
s =~ /^\d+$/
end
# 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.
def self.sort(arr)
arr.sort do |a, b|
a_digits = all_digits?(a)
b_digits = all_digits?(b)
if a_digits && b_digits
Integer(a) <=> Integer(b)
elsif a_digits
-1
elsif b_digits
1
else
a <=> b
end
end
end
end
def render_value_to_sb(sb, indent, at_root, options)
if empty?
sb << "{}"
else
outer_braces = options.json? || !at_root
if outer_braces
inner_indent = indent + 1
sb << "{"
if options.formatted?
sb << "\n"
end
else
inner_indent = indent
end
separator_count = 0
sorted_keys = RenderComparator.sort(keys)
sorted_keys.each do |k|
v = @value[k]
if options.origin_comments?
lines = v.origin.description.split("\n")
lines.each { |l|
Hocon::Impl::AbstractConfigValue.indent(sb, indent + 1, options)
sb << '#'
unless l.empty?
sb << ' '
end
sb << l
sb << "\n"
}
end
if options.comments?
v.origin.comments.each do |comment|
Hocon::Impl::AbstractConfigValue.indent(sb, inner_indent, options)
sb << "#"
if !comment.start_with?(" ")
sb << " "
end
sb << comment
sb << "\n"
end
end
Hocon::Impl::AbstractConfigValue.indent(sb, inner_indent, options)
v.render_to_sb(sb, inner_indent, false, k.to_s, options)
if options.formatted?
if options.json?
sb << ","
separator_count = 2
else
separator_count = 1
end
sb << "\n"
else
sb << ","
separator_count = 1
end
end
# chop last commas/newlines
# couldn't figure out a better way to chop characters off of the end of
# the StringIO. This relies on making sure that, prior to returning the
# final string, we take a substring that ends at sb.pos.
sb.pos = sb.pos - separator_count
if outer_braces
if options.formatted?
sb << "\n" # put a newline back
if outer_braces
Hocon::Impl::AbstractConfigValue.indent(sb, indent, options)
end
end
sb << "}"
end
end
if at_root && options.formatted?
sb << "\n"
end
end
def self.map_equals(a, b)
if a.equal?(b)
return true
end
# Hashes aren't ordered in ruby, so sort first
if not a.keys.sort == b.keys.sort
return false
end
a.keys.each do |key|
if a[key] != b[key]
return false
end
end
true
end
def get(key)
@value[key]
end
def self.map_hash(m)
# the keys have to be sorted, otherwise we could be equal
# to another map but have a different hashcode.
keys = m.keys.sort
value_hash = 0
keys.each do |key|
value_hash += m[key].hash
end
41 * (41 + keys.hash) + value_hash
end
def can_equal(other)
other.is_a? Hocon::ConfigObject
end
def ==(other)
# note that "origin" is deliberately NOT part of equality.
# neither are other "extras" like ignoresFallbacks or resolve status.
if other.is_a? Hocon::ConfigObject
# optimization to avoid unwrapped() for two ConfigObject,
# which is what AbstractConfigValue does.
can_equal(other) && self.class.map_equals(self, other)
else
false
end
end
def hash
self.class.map_hash(@value)
end
def contains_key?(key)
@value.has_key?(key)
end
def key_set
Set.new(@value.keys)
end
def contains_value?(v)
@value.has_value?(v)
end
def self.empty(origin = nil)
if origin.nil?
empty(Hocon::Impl::SimpleConfigOrigin.new_simple("empty config"))
else
self.new(origin, {})
end
end
def self.empty_missing(base_origin)
self.new(
Hocon::Impl::SimpleConfigOrigin.new_simple("#{base_origin.description} (not found)"),
{})
end
end
hocon-1.3.1/lib/hocon/impl/resolve_memos.rb 0000644 0000041 0000041 00000000455 13704151031 020672 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/impl'
class Hocon::Impl::ResolveMemos
def initialize(memos = {})
@memos = memos
end
def get(key)
@memos[key]
end
def put(key, value)
copy = @memos.clone
copy[key] = value
Hocon::Impl::ResolveMemos.new(copy)
end
end
hocon-1.3.1/lib/hocon/impl/simple_includer.rb 0000644 0000041 0000041 00000015161 13704151031 021171 0 ustar www-data www-data # encoding: utf-8
require 'stringio'
require 'hocon/impl'
require 'hocon/impl/full_includer'
require 'hocon/impl/url'
require 'hocon/impl/config_impl'
require 'hocon/config_error'
require 'hocon/config_syntax'
require 'hocon/impl/simple_config_object'
require 'hocon/impl/simple_config_origin'
require 'hocon/config_includer_file'
require 'hocon/config_factory'
require 'hocon/impl/parseable'
class Hocon::Impl::SimpleIncluder < Hocon::Impl::FullIncluder
ConfigBugorBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
ConfigIOError = Hocon::ConfigError::ConfigIOError
SimpleConfigObject = Hocon::Impl::SimpleConfigObject
SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin
def initialize(fallback)
@fallback = fallback
end
# ConfigIncludeContext does this for us on its options
def self.clear_for_include(options)
# the class loader and includer are inherited, but not this other stuff
options.set_syntax(nil).set_origin_description(nil).set_allow_missing(true)
end
# this is the heuristic includer
def include(context, name)
obj = self.class.include_without_fallback(context, name)
# now use the fallback includer if any and merge its result
if ! (@fallback.nil?)
obj.with_fallback(@fallback.include(context, name))
else
obj
end
end
# the heuristic includer in static form
def self.include_without_fallback(context, name)
# the heuristic is valid URL then URL, else relative to including file;
# relativeTo in a file falls back to classpath inside relativeTo().
url = nil
begin
url = Hocon::Impl::Url.new(name)
rescue Hocon::Impl::Url::MalformedUrlError => e
url = nil
end
if !(url.nil?)
include_url_without_fallback(context, url)
else
source = RelativeNameSource.new(context)
from_basename(source, name, context.parse_options)
end
end
# NOTE: not porting `include_url` or `include_url_without_fallback` from upstream,
# because we probably won't support URL includes for now.
def include_file(context, file)
obj = self.class.include_file_without_fallback(context, file)
# now use the fallback includer if any and merge its result
if (!@fallback.nil?) && @fallback.is_a?(Hocon::ConfigIncluderFile)
obj.with_fallback(@fallback).include_file(context, file)
else
obj
end
end
def self.include_file_without_fallback(context, file)
Hocon::ConfigFactory.parse_file_any_syntax(file, context.parse_options).root
end
# NOTE: not porting `include_resources` or `include_resources_without_fallback`
# for now because we're not going to support looking for things on the ruby
# load path for now.
def with_fallback(fallback)
if self.equal?(fallback)
raise ConfigBugOrBrokenError, "trying to create includer cycle"
elsif @fallback.equal?(fallback)
self
elsif @fallback.nil?
self.class.new(@fallback.with_fallback(fallback))
else
self.class.new(fallback)
end
end
class NameSource
def name_to_parseable(name, parse_options)
raise Hocon::ConfigError::ConfigBugOrBrokenError,
"name_to_parseable must be implemented by subclass (#{self.class})"
end
end
class RelativeNameSource < NameSource
def initialize(context)
@context = context
end
def name_to_parseable(name, options)
p = @context.relative_to(name)
if p.nil?
# avoid returning nil
Hocon::Impl::Parseable.new_not_found(name, "include was not found: '#{name}'", options)
else
p
end
end
end
# 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.
def self.from_basename(source, name, options)
obj = nil
if name.end_with?(".conf") || name.end_with?(".json") || name.end_with?(".properties")
p = source.name_to_parseable(name, options)
obj = p.parse(p.options.set_allow_missing(options.allow_missing?))
else
conf_handle = source.name_to_parseable(name + ".conf", options)
json_handle = source.name_to_parseable(name + ".json", options)
got_something = false
fails = []
syntax = options.syntax
obj = SimpleConfigObject.empty(SimpleConfigOrigin.new_simple(name))
if syntax.nil? || (syntax == Hocon::ConfigSyntax::CONF)
begin
obj = conf_handle.parse(conf_handle.options.set_allow_missing(false).
set_syntax(Hocon::ConfigSyntax::CONF))
got_something = true
rescue ConfigIOError => e
fails << e
end
end
if syntax.nil? || (syntax == Hocon::ConfigSyntax::JSON)
begin
parsed = json_handle.parse(json_handle.options.set_allow_missing(false).
set_syntax(Hocon::ConfigSyntax::JSON))
obj = obj.with_fallback(parsed)
got_something = true
rescue ConfigIOError => e
fails << e
end
end
# NOTE: skipping the upstream block here that would attempt to parse
# a java properties file.
if (! options.allow_missing?) && (! got_something)
if Hocon::Impl::ConfigImpl.trace_loads_enabled
# the individual exceptions should have been logged already
# with tracing enabled
Hocon::Impl::ConfigImpl.trace("Did not find '#{name}'" +
" with any extension (.conf, .json, .properties); " +
"exceptions should have been logged above.")
end
if fails.empty?
# this should not happen
raise ConfigBugOrBrokenError, "should not be reached: nothing found but no exceptions thrown"
else
sb = StringIO.new
fails.each do |t|
sb << t
sb << ", "
end
raise ConfigIOError.new(SimpleConfigOrigin.new_simple(name), sb.string, fails[0])
end
elsif !got_something
if Hocon::Impl::ConfigImpl.trace_loads_enabled
Hocon::Impl::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.")
end
end
end
obj
end
class Proxy < Hocon::Impl::FullIncluder
def initialize(delegate)
@delegate = delegate
end
## TODO: port remaining implementation when needed
end
def self.make_full(includer)
if includer.is_a?(Hocon::Impl::FullIncluder)
includer
else
Proxy.new(includer)
end
end
end
hocon-1.3.1/lib/hocon/impl/simple_include_context.rb 0000644 0000041 0000041 00000001442 13704151031 022550 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/simple_includer'
require 'hocon/config_include_context'
require 'hocon/impl/config_impl'
class Hocon::Impl::SimpleIncludeContext
include Hocon::ConfigIncludeContext
def initialize(parseable)
@parseable = parseable
end
def with_parseable(parseable)
if parseable.equal?(@parseable)
self
else
self.class.new(parseable)
end
end
def relative_to(filename)
if Hocon::Impl::ConfigImpl.trace_loads_enabled
Hocon::Impl::ConfigImpl.trace("Looking for '#{filename}' relative to #{@parseable}")
end
if ! @parseable.nil?
@parseable.relative_to(filename)
else
nil
end
end
def parse_options
Hocon::Impl::SimpleIncluder.clear_for_include(@parseable.options)
end
end
hocon-1.3.1/lib/hocon/impl/array_iterator.rb 0000644 0000041 0000041 00000000345 13704151031 021040 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
class Hocon::Impl::ArrayIterator
def initialize(a)
@a = a
@index = 0
end
def has_next?
@index < @a.length
end
def next
@index += 1
@a[@index - 1]
end
end
hocon-1.3.1/lib/hocon/impl/tokens.rb 0000644 0000041 0000041 00000023553 13704151031 017322 0 ustar www-data www-data # encoding: utf-8
require 'stringio'
require 'hocon/impl'
require 'hocon/impl/token'
require 'hocon/impl/token_type'
require 'hocon/impl/config_number'
require 'hocon/impl/config_string'
require 'hocon/impl/config_null'
require 'hocon/impl/config_boolean'
require 'hocon/config_error'
require 'hocon/impl/resolve_status'
require 'hocon/config_value_type'
# FIXME the way the subclasses of Token are private with static isFoo and accessors is kind of ridiculous.
class Hocon::Impl::Tokens
Token = Hocon::Impl::Token
TokenType = Hocon::Impl::TokenType
ConfigNumber = Hocon::Impl::ConfigNumber
ConfigInt = Hocon::Impl::ConfigInt
ConfigDouble = Hocon::Impl::ConfigDouble
ConfigString = Hocon::Impl::ConfigString
ConfigNull = Hocon::Impl::ConfigNull
ConfigBoolean = Hocon::Impl::ConfigBoolean
ResolveStatus = Hocon::Impl::ResolveStatus
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
class Value < Token
def initialize(value, orig_text = nil)
super(TokenType::VALUE, value.origin, orig_text)
@value = value
end
attr_reader :value
def to_s
if value.resolve_status == ResolveStatus::RESOLVED
"'#{value.unwrapped}' (#{Hocon::ConfigValueType.value_type_name(value.value_type)})"
else
"'' ((#{Hocon::ConfigValueType.value_type_name(value.value_type)})"
end
end
def can_equal(o)
o.is_a?(Value)
end
def ==(other)
super(other) && other.value == @value
end
def hash
41 * (41 + super) + value.hash
end
end
class Line < Token
def initialize(origin)
super(TokenType::NEWLINE, origin)
end
def can_equal(other)
o.is_a?(Line)
end
def ==(other)
super(other) && other.line_number == line_number
end
def hash
41 * (41 + super) + line_number
end
def token_text
"\n"
end
end
class UnquotedText < Token
def initialize(origin, s)
super(TokenType::UNQUOTED_TEXT, origin)
@value = s
end
attr_reader :value
def to_s
"'#{value}'"
end
def can_equal(o)
o.is_a?(UnquotedText)
end
def ==(other)
super(other) && other.value == @value
end
def hash
41 * (41 + super) + value.hash
end
def token_text
@value
end
end
class IgnoredWhitespace < Token
def initialize(origin, s)
super(TokenType::IGNORED_WHITESPACE, origin)
@value = s
end
attr_reader :value
def to_s
"'#{value}' (WHITESPACE)"
end
def can_equal(o)
o.is_a?(IgnoredWhitespace)
end
def ==(other)
super(other) && other.value == value
end
def hash
41 * (41 + super) + value.hash
end
def token_text
@value
end
end
class Problem < Token
def initialize(origin, what, message, suggest_quotes, cause)
super(TokenType::PROBLEM, origin)
@what = what
@message = message
@suggest_quotes = suggest_quotes
@cause = cause
end
def what
@what
end
def message
@message
end
def suggest_quotes
@suggest_quotes
end
def cause
@cause
end
def to_s
sb = StringIO.new
sb << "'"
sb << what
sb << "'"
sb << " ("
sb << message
sb << ")"
sb.string
end
def can_equal(other)
other.is_a?(Problem)
end
def ==(other)
super(other) && other.what == @what &&
other.message == @message &&
other.suggest_quotes == @suggest_quotes &&
Hocon::Impl::ConfigImplUtil.equals_handling_nil?(other.cause, @cause)
end
def hash
hashcode = 41 * (41 + super)
hashcode = 41 * (hashcode + @what.hash)
hashcode = 41 * (hashcode + @message.hash)
hashcode = 41 * (hashcode + @suggest_quotes.hash)
unless @cause.nil?
hashcode = 41 * (hashcode + @cause.hash)
end
hashcode
end
end
class Comment < Token
def initialize(origin, text)
super(TokenType::COMMENT, origin)
@text = text
end
class DoubleSlashComment < Comment
def initialize(origin, text)
super(origin, text)
end
def token_text
"//" + @text
end
end
class HashComment < Comment
def initialize(origin, text)
super(origin, text)
end
def token_text
"#" + @text
end
end
attr_reader :text
def to_s
sb = StringIO.new
sb << "'#"
sb << text
sb << "' (COMMENT)"
sb.string
end
def can_equal(other)
other.is_a?(Comment)
end
def ==(other)
super(other) && other.text == @text
end
def hash
hashcode = 41 * (41 + super)
hashcode = 41 * (hashcode + @text.hash)
hashcode
end
end
# This is not a Value, because it requires special processing
class Substitution < Token
def initialize(origin, optional, expression)
super(TokenType::SUBSTITUTION, origin)
@optional = optional
@value = expression
end
def optional?
@optional
end
attr_reader :value
def token_text
sub_text = ""
@value.each { |t| sub_text << t.token_text }
"${" + (@optional ? "?" : "") + sub_text + "}"
end
def to_s
sb = StringIO.new
value.each do |t|
sb << t.to_s
end
"'${#{sb.to_s}}'"
end
def can_equal(other)
other.is_a?(Substitution)
end
def ==(other)
super(other) && other.value == @value
end
def hash
41 * (41 + super + @value.hash)
end
end
def self.value?(token)
token.is_a?(Value)
end
def self.value(token)
if token.is_a?(Value)
token.value
else
raise ConfigBugOrBrokenError, "tried to get value of non-value token #{token}"
end
end
def self.value_with_type?(t, value_type)
value?(t) && (value(t).value_type == value_type)
end
def self.newline?(t)
t.is_a?(Line)
end
def self.problem?(t)
t.is_a?(Problem)
end
def self.get_problem_what(token)
if token.is_a?(Problem)
token.what
else
raise ConfigBugOrBrokenError, "tried to get problem what from #{token}"
end
end
def self.get_problem_message(token)
if token.is_a?(Problem)
token.message
else
raise ConfigBugOrBrokenError.new("tried to get problem message from #{token}")
end
end
def self.get_problem_suggest_quotes(token)
if token.is_a?(Problem)
token.suggest_quotes
else
raise ConfigBugOrBrokenError.new("tried to get problem suggest_quotes from #{token}")
end
end
def self.get_problem_cause(token)
if token.is_a?(Problem)
token.cause
else
raise ConfigBugOrBrokenError.new("tried to get problem cause from #{token}")
end
end
def self.comment?(t)
t.is_a?(Comment)
end
def self.comment_text(token)
if comment?(token)
token.text
else
raise ConfigBugOrBrokenError, "tried to get comment text from #{token}"
end
end
def self.unquoted_text?(token)
token.is_a?(UnquotedText)
end
def self.unquoted_text(token)
if unquoted_text?(token)
token.value
else
raise ConfigBugOrBrokenError, "tried to get unquoted text from #{token}"
end
end
def self.ignored_whitespace?(token)
token.is_a?(IgnoredWhitespace)
end
def self.substitution?(token)
token.is_a?(Substitution)
end
def self.get_substitution_path_expression(token)
if token.is_a?(Substitution)
token.value
else
raise ConfigBugOrBrokenError, "tried to get substitution from #{token}"
end
end
def self.get_substitution_optional(token)
if token.is_a?(Substitution)
token.optional?
else
raise ConfigBugOrBrokenError, "tried to get substitution optionality from #{token}"
end
end
START = Token.new_without_origin(TokenType::START, "start of file", "")
EOF = Token.new_without_origin(TokenType::EOF, "end of file", "")
COMMA = Token.new_without_origin(TokenType::COMMA, "','", ",")
EQUALS = Token.new_without_origin(TokenType::EQUALS, "'='", "=")
COLON = Token.new_without_origin(TokenType::COLON, "':'", ":")
OPEN_CURLY = Token.new_without_origin(TokenType::OPEN_CURLY, "'{'", "{")
CLOSE_CURLY = Token.new_without_origin(TokenType::CLOSE_CURLY, "'}'", "}")
OPEN_SQUARE = Token.new_without_origin(TokenType::OPEN_SQUARE, "'['", "[")
CLOSE_SQUARE = Token.new_without_origin(TokenType::CLOSE_SQUARE, "']'", "]")
PLUS_EQUALS = Token.new_without_origin(TokenType::PLUS_EQUALS, "'+='", "+=")
def self.new_line(origin)
Line.new(origin)
end
def self.new_problem(origin, what, message, suggest_quotes, cause)
Problem.new(origin, what, message, suggest_quotes, cause)
end
def self.new_comment_double_slash(origin, text)
Comment::DoubleSlashComment.new(origin, text)
end
def self.new_comment_hash(origin, text)
Comment::HashComment.new(origin, text)
end
def self.new_unquoted_text(origin, s)
UnquotedText.new(origin, s)
end
def self.new_ignored_whitespace(origin, s)
IgnoredWhitespace.new(origin, s)
end
def self.new_substitution(origin, optional, expression)
Substitution.new(origin, optional, expression)
end
def self.new_value(value, orig_text = nil)
Value.new(value, orig_text)
end
def self.new_string(origin, value, orig_text)
new_value(ConfigString::Quoted.new(origin, value), orig_text)
end
def self.new_int(origin, value, orig_text)
new_value(ConfigNumber.new_number(origin, value, orig_text), orig_text)
end
def self.new_double(origin, value, orig_text)
new_value(ConfigNumber.new_number(origin, value, orig_text), orig_text)
end
def self.new_long(origin, value, orig_text)
new_value(ConfigNumber.new_number(origin, value, orig_text), orig_text)
end
def self.new_null(origin)
new_value(ConfigNull.new(origin), "null")
end
def self.new_boolean(origin, value)
new_value(ConfigBoolean.new(origin, value), value.to_s)
end
end
hocon-1.3.1/lib/hocon/impl/config_reference.rb 0000644 0000041 0000041 00000010066 13704151031 021275 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/impl'
require 'hocon/impl/abstract_config_value'
class Hocon::Impl::ConfigReference
include Hocon::Impl::Unmergeable
include Hocon::Impl::AbstractConfigValue
# Require these lazily, to avoid circular dependencies
require 'hocon/impl/resolve_source'
require 'hocon/impl/resolve_result'
NotPossibleToResolve = Hocon::Impl::AbstractConfigValue::NotPossibleToResolve
UnresolvedSubstitutionError = Hocon::ConfigError::UnresolvedSubstitutionError
attr_reader :expr, :prefix_length
def initialize(origin, expr, prefix_length = 0)
super(origin)
@expr = expr
@prefix_length = prefix_length
end
def unmerged_values
[self]
end
# 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.
def resolve_substitutions(context, source)
new_context = context.add_cycle_marker(self)
begin
result_with_path = source.lookup_subst(new_context, @expr, @prefix_length)
new_context = result_with_path.result.context
if result_with_path.result.value != nil
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"recursively resolving #{result_with_path} which was the resolution of #{expr} against #{source}",
context.depth)
end
recursive_resolve_source = Hocon::Impl::ResolveSource.new(
result_with_path.path_from_root.last, result_with_path.path_from_root)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("will recursively resolve against #{recursive_resolve_source}", context.depth)
end
result = new_context.resolve(result_with_path.result.value,
recursive_resolve_source)
v = result.value
new_context = result.context
else
v = nil
end
rescue NotPossibleToResolve => e
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"not possible to resolve #{expr}, cycle involved: #{e.trace_string}", new_context.depth)
end
if @expr.optional
v = nil
else
raise UnresolvedSubstitutionError.new(
origin,
"#{@expr} was part of a cycle of substitutions involving #{e.trace_string}", e)
end
end
if v == nil && !@expr.optional
if new_context.options.allow_unresolved
ResolveResult.make(new_context.remove_cycle_marker(self), self)
else
raise UnresolvedSubstitutionError.new(origin, @expr.to_s)
end
else
Hocon::Impl::ResolveResult.make(new_context.remove_cycle_marker(self), v)
end
end
def value_type
raise not_resolved
end
def unwrapped
raise not_resolved
end
def new_copy(new_origin)
Hocon::Impl::ConfigReference.new(new_origin, @expr, @prefix_length)
end
def ignores_fallbacks?
false
end
def resolve_status
Hocon::Impl::ResolveStatus::UNRESOLVED
end
def relativized(prefix)
new_expr = @expr.change_path(@expr.path.prepend(prefix))
Hocon::Impl::ConfigReference.new(origin, new_expr, @prefix_length + prefix.length)
end
def can_equal(other)
other.is_a? Hocon::Impl::ConfigReference
end
def ==(other)
# note that "origin" is deliberately NOT part of equality
if other.is_a? Hocon::Impl::ConfigReference
can_equal(other) && @expr == other.expr
end
end
def hash
# note that "origin" is deliberately NOT part of equality
@expr.hash
end
def render_value_to_sb(sb, indent, at_root, options)
sb << @expr.to_s
end
def expression
@expr
end
private
def not_resolved
error_message = "need to Config#resolve, see the API docs for Config#resolve; substitution not resolved: #{self}"
Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil)
end
end
hocon-1.3.1/lib/hocon/impl/config_delayed_merge.rb 0000644 0000041 0000041 00000023676 13704151031 022140 0 ustar www-data www-data require 'hocon/impl'
require 'hocon/impl/replaceable_merge_stack'
require 'hocon/impl/config_delayed_merge_object'
require 'hocon/impl/config_impl'
require 'hocon/impl/resolve_result'
require 'hocon/impl/abstract_config_value'
#
# 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.
#
class Hocon::Impl::ConfigDelayedMerge
include Hocon::Impl::Unmergeable
include Hocon::Impl::ReplaceableMergeStack
include Hocon::Impl::AbstractConfigValue
ConfigImpl = Hocon::Impl::ConfigImpl
ResolveResult = Hocon::Impl::ResolveResult
def initialize(origin, stack)
super(origin)
@stack = stack
if stack.empty?
raise Hocon::ConfigError::ConfigBugOrBrokenError.new("creating empty delayed merge value", nil)
end
stack.each do |v|
if v.is_a?(Hocon::Impl::ConfigDelayedMerge) || v.is_a?(Hocon::Impl::ConfigDelayedMergeObject)
error_message = "placed nested DelayedMerge in a ConfigDelayedMerge, should have consolidated stack"
raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil)
end
end
end
attr_reader :stack
def value_type
error_message = "called value_type() on value with unresolved substitutions, need to Config#resolve() first, see API docs"
raise Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil)
end
def unwrapped
error_message = "called unwrapped() on value with unresolved substitutions, need to Config#resolve() first, see API docs"
raise Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil)
end
def resolve_substitutions(context, source)
self.class.resolve_substitutions(self, stack, context, source)
end
def self.resolve_substitutions(replaceable, stack, context, source)
if ConfigImpl.trace_substitution_enabled
ConfigImpl.trace("delayed merge stack has #{stack.size} items:", context.depth)
count = 0
stack.each do |v|
ConfigImpl.trace("#{count}: #{v}", context.depth)
count += 1
end
end
# 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.
new_context = context
count = 0
merged = nil
stack.each do |stack_end|
# the end value may or may not be resolved already
if stack_end.is_a?(Hocon::Impl::ReplaceableMergeStack)
raise ConfigBugOrBrokenError, "A delayed merge should not contain another one: #{replaceable}"
elsif stack_end.is_a?(Hocon::Impl::Unmergeable)
# the remainder could be any kind of value, including another
# ConfigDelayedMerge
remainder = replaceable.make_replacement(context, count + 1)
if ConfigImpl.trace_substitution_enabled
ConfigImpl.trace("remainder portion: #{remainder}", new_context.depth)
end
# 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 ConfigImpl.trace_substitution_enabled
ConfigImpl.trace("building sourceForEnd", new_context.depth)
end
# we resetParents() here because we'll be resolving "end"
# against a root which does NOT contain "end"
source_for_end = source.replace_within_current_parent(replaceable, remainder)
if ConfigImpl.trace_substitution_enabled
ConfigImpl.trace(" sourceForEnd before reset parents but after replace: #{source_for_end}", new_context.depth)
end
source_for_end = source_for_end.reset_parents
else
if ConfigImpl.trace_substitution_enabled
ConfigImpl.trace("will resolve end against the original source with parent pushed",
new_context.depth)
end
source_for_end = source.push_parent(replaceable)
end
if ConfigImpl.trace_substitution_enabled
ConfigImpl.trace("sourceForEnd =#{source_for_end}", new_context.depth)
end
if ConfigImpl.trace_substitution_enabled
ConfigImpl.trace("Resolving highest-priority item in delayed merge #{stack_end}" +
" against #{source_for_end} endWasRemoved=#{(source != source_for_end)}")
end
result = new_context.resolve(stack_end, source_for_end)
resolved_end = result.value
new_context = result.context
if ! resolved_end.nil?
if merged.nil?
merged = resolved_end
else
if ConfigImpl.trace_substitution_enabled
ConfigImpl.trace("merging #{merged} with fallback #{resolved_end}",
new_context.depth + 1)
end
merged = merged.with_fallback(resolved_end)
end
end
count += 1
if ConfigImpl.trace_substitution_enabled
ConfigImpl.trace("stack merged, yielding: #{merged}",
new_context.depth)
end
end
ResolveResult.make(new_context, merged)
end
def make_replacement(context, skipping)
self.class.make_replacement(context, @stack, skipping)
end
# static method also used by ConfigDelayedMergeObject; end may be null
def self.make_replacement(context, stack, skipping)
sub_stack = stack.slice(skipping..stack.size)
if sub_stack.empty?
if ConfigImpl.trace_substitution_enabled
ConfigImpl.trace("Nothing else in the merge stack, replacing with null", context.depth)
return nil
end
else
# generate a new merge stack from only the remaining items
merged = nil
sub_stack.each do |v|
if merged.nil?
merged = v
else
merged = merged.with_fallback(v)
end
end
merged
end
end
def resolve_status
Hocon::Impl::ResolveStatus::UNRESOLVED
end
def replace_child(child, replacement)
new_stack = replace_child_in_list(stack, child, replacement)
if new_stack.nil?
nil
else
self.class.new(origin, new_stack)
end
end
def has_descendant?(descendant)
Hocon::Impl::AbstractConfigValue.has_descendant_in_list?(stack, descendant)
end
def relativized(prefix)
new_stack = stack.map { |o| o.relativized(prefix) }
self.class.new(origin, new_stack)
end
# static utility shared with ConfigDelayedMergeObject
def self.stack_ignores_fallbacks?(stack)
last = stack[-1]
last.ignores_fallbacks?
end
def ignores_fallbacks?
self.class.stack_ignores_fallbacks?(stack)
end
def new_copy(new_origin)
self.class.new(new_origin, stack)
end
def merged_with_the_unmergeable(fallback)
merged_stack_with_the_unmergeable(stack, fallback)
end
def merged_with_object(fallback)
merged_stack_with_object(stack, fallback)
end
def merged_with_non_object(fallback)
merged_stack_with_non_object(stack, fallback)
end
def unmerged_values
stack
end
def can_equal(other)
other.is_a? Hocon::Impl::ConfigDelayedMerge
end
def ==(other)
# note that "origin" is deliberately NOT part of equality
if other.is_a? Hocon::Impl::ConfigDelayedMerge
can_equal(other) && (@stack == other.stack || @stack.equal?(other.stack))
else
false
end
end
def hash
# note that "origin" is deliberately NOT part of equality
@stack.hash
end
def render_to_sb(sb, indent, at_root, at_key, options)
self.class.render_value_to_sb_from_stack(stack, sb, indent, at_root, at_key, options)
end
# static method also used by ConfigDelayedMergeObject.
def self.render_value_to_sb_from_stack(stack, sb, indent, at_root, at_key, options)
comment_merge = options.comments
if comment_merge
sb << "# unresolved merge of #{stack.size} values follows (\n"
if at_key.nil?
self.indent(sb, indent, options)
sb << "# this unresolved merge will not be parseable because it's at the root of the object\n"
self.indent(sb, indent, options)
sb << "# the HOCON format has no way to list multiple root objects in a single file\n"
end
end
reversed = stack.reverse
i = 0
reversed.each do |v|
if comment_merge
self.indent(sb, indent, options)
if !at_key.nil?
rendered_key = Hocon::Impl::ConfigImplUtil.render_json_string(at_key)
sb << "# unmerged value #{i} for key #{rendered_key}"
else
sb << "# unmerged value #{i} from "
end
i += 1
sb << v.origin.description
sb << "\n"
v.origin.comments.each do |comment|
self.indent(sb, indent, options)
sb << "# "
sb << comment
sb << "\n"
end
end
Hocon::Impl::AbstractConfigValue.indent(sb, indent, options)
if !at_key.nil?
sb << Hocon::Impl::ConfigImplUtil.render_json_string(at_key)
if options.formatted
sb << ": "
else
sb << ":"
end
end
v.render_value_to_sb(sb, indent, at_root, options)
sb << ","
if options.formatted
sb.append "\n"
end
end
# chop comma or newline
# couldn't figure out a better way to chop characters off of the end of
# the StringIO. This relies on making sure that, prior to returning the
# final string, we take a substring that ends at sb.pos.
sb.pos = sb.pos - 1
if options.formatted
sb.pos = sb.pos - 1
sb << "\n"
end
if comment_merge
self.indent(sb, indent, options)
sb << "# ) end of unresolved merge\n"
end
end
end
hocon-1.3.1/lib/hocon/impl/config_impl_util.rb 0000644 0000041 0000041 00000005000 13704151031 021325 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'stringio'
class Hocon::Impl::ConfigImplUtil
def self.equals_handling_nil?(a, b)
# This method probably doesn't make any sense in ruby... not sure
if a.nil? && !b.nil?
false
elsif !a.nil? && b.nil?
false
# in ruby, the == and .equal? are the opposite of what they are in Java
elsif a.equal?(b)
true
else
a == b
end
end
#
# This is public ONLY for use by the "config" package, DO NOT USE this ABI
# may change.
#
def self.render_json_string(s)
sb = StringIO.new
sb << '"'
s.chars.each do |c|
case c
when '"' then sb << "\\\""
when "\\" then sb << "\\\\"
when "\n" then sb << "\\n"
when "\b" then sb << "\\b"
when "\f" then sb << "\\f"
when "\r" then sb << "\\r"
when "\t" then sb << "\\t"
else
if c =~ /[[:cntrl:]]/
sb << ("\\u%04x" % c)
else
sb << c
end
end
end
sb << '"'
sb.string
end
def self.render_string_unquoted_if_possible(s)
# this can quote unnecessarily as long as it never fails to quote when
# necessary
if s.length == 0
return render_json_string(s)
end
# if it starts with a hyphen or number, we have to quote
# to ensure we end up with a string and not a number
first = s.chars.first
if (first =~ /[[:digit:]]/) || (first == '-')
return render_json_string(s)
end
# only unquote if it's pure alphanumeric
s.chars.each do |c|
unless (c =~ /[[:alnum:]]/) || (c == '-')
return render_json_string(s)
end
end
s
end
def self.join_path(*elements)
Hocon::Impl::Path.from_string_list(elements).render
end
def self.split_path(path)
p = Hocon::Impl::Path.new_path(path)
elements = []
until p.nil?
elements << p.first
p = p.remainder
end
elements
end
def self.whitespace?(c)
# this implementation is *not* a port of the java code, because it relied on
# the method java.lang.Character#isWhitespace. This is probably
# insanely slow (running a regex against every single character in the
# file).
c =~ /[[:space:]]/
end
def self.unicode_trim(s)
# this implementation is *not* a port of the java code. Ruby can strip
# unicode whitespace much easier than Java can, and relies on a lot of
# Java functions that don't really have straight equivalents in Ruby.
s.gsub(/[:space]/, ' ')
s.strip
end
end
hocon-1.3.1/lib/hocon/impl/config_node_root.rb 0000644 0000041 0000041 00000004172 13704151031 021330 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/config_node_array'
require 'hocon/impl/config_node_complex_value'
require 'hocon/impl/config_node_object'
class Hocon::Impl::ConfigNodeRoot
include Hocon::Impl::ConfigNodeComplexValue
def initialize(children, origin)
super(children)
@origin = origin
end
def new_node(nodes)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "Tried to indent the root object"
end
def value
@children.each do |node|
if node.is_a?(Hocon::Impl::ConfigNodeComplexValue)
return node
end
end
raise Hocon::ConfigError::ConfigBugOrBrokenError, "ConfigNodeRoot did not contain a value"
end
def set_value(desired_path, value, flavor)
children_copy = @children.clone
children_copy.each_with_index do |node, index|
if node.is_a?(Hocon::Impl::ConfigNodeComplexValue)
if node.is_a?(Hocon::Impl::ConfigNodeArray)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "The ConfigDocument had an array at the root level, and values cannot be modified inside an array."
elsif node.is_a?(Hocon::Impl::ConfigNodeObject)
if value.nil?
children_copy[index] = node.remove_value_on_path(desired_path, flavor)
else
children_copy[index] = node.set_value_on_path(desired_path, value, flavor)
end
return self.class.new(children_copy, @origin)
end
end
end
raise Hocon::ConfigError::ConfigBugOrBrokenError, "ConfigNodeRoot did not contain a value"
end
def has_value(desired_path)
path = Hocon::Impl::PathParser.parse_path(desired_path)
@children.each do |node|
if node.is_a?(Hocon::Impl::ConfigNodeComplexValue)
if node.is_a?(Hocon::Impl::ConfigNodeArray)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "The ConfigDocument had an array at the root level, and values cannot be modified inside an array."
elsif node.is_a?(Hocon::Impl::ConfigNodeObject)
return node.has_value(path)
end
end
end
raise Hocon::ConfigError::ConfigBugOrBrokenError, "ConfigNodeRoot did not contain a value"
end
end
hocon-1.3.1/lib/hocon/impl/resolve_source.rb 0000644 0000041 0000041 00000026156 13704151031 021060 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_error'
require 'hocon/impl'
require 'hocon/impl/config_impl'
require 'hocon/impl/container'
class Hocon::Impl::ResolveSource
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError
# 'path_from_root' 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.
attr_accessor :root, :path_from_root
def initialize(root, path_from_root = nil)
@root = root
@path_from_root = path_from_root
end
# 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.
def find_in_object(obj, context, path)
# resolve ONLY portions of the object which are along our path
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("*** finding '#{path}' in #{obj}")
end
restriction = context.restrict_to_child
partially_resolved = context.restrict(path).resolve(obj, self.class.new(obj))
new_context = partially_resolved.context.restrict(restriction)
if partially_resolved.value.is_a?(Hocon::Impl::AbstractConfigObject)
pair = self.class.find_in_object_impl(partially_resolved.value, path)
ResultWithPath.new(Hocon::Impl::ResolveResult.make(new_context, pair.value), pair.path_from_root)
else
raise ConfigBugOrBrokenError.new("resolved object to non-object " + obj + " to " + partially_resolved)
end
end
def lookup_subst(context, subst, prefix_length)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("searching for #{subst}", context.depth)
end
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("#{subst} - looking up relative to file it occurred in",
context.depth)
end
# First we look up the full path, which means relative to the
# included file if we were not a root file
result = find_in_object(@root, context, subst.path)
if result.result.value == nil
# 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.
unprefixed = subst.path.sub_path_to_end(prefix_length)
if prefix_length > 0
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
unprefixed + " - looking up relative to parent file",
result.result.context.depth)
end
result = find_in_object(@root, result.result.context, unprefixed)
end
if result.result.value == nil && result.result.context.options.use_system_environment
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"#{unprefixed} - looking up in system environment",
result.result.context.depth)
end
result = find_in_object(Hocon::Impl::ConfigImpl.env_variables_as_config_object, context, unprefixed)
end
end
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"resolved to #{result}",
result.result.context.depth)
end
result
end
def push_parent(parent)
unless parent
raise ConfigBugOrBrokenError.new("can't push null parent")
end
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("pushing parent #{parent} ==root #{(parent == root)} onto #{self}")
end
if @path_from_root == nil
if parent.equal?(@root)
return self.class.new(@root, Node.new(parent))
else
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
# this hasDescendant check is super-expensive so it's a
# trace message rather than an assertion
if @root.has_descendant?(parent)
Hocon::Impl::ConfigImpl.trace(
"***** BUG ***** tried to push parent #{parent} without having a path to it in #{self}")
end
end
# ignore parents if we aren't proceeding from the
# root
return self
end
else
parent_parent = @path_from_root.head
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
# this hasDescendant check is super-expensive so it's a
# trace message rather than an assertion
if parent_parent != nil && !parent_parent.has_descendant?(parent)
Hocon::Impl::ConfigImpl.trace(
"***** BUG ***** trying to push non-child of #{parent_parent}, non-child was #{parent}")
end
end
self.class.new(@root, @path_from_root.prepend(parent))
end
end
def reset_parents
if @path_from_root == nil
this
else
self.class.new(@root)
end
end
def replace_current_parent(old, replacement)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("replaceCurrentParent old #{old}@#{old.hash} replacement " +
"#{replacement}@#{old.hash} in #{self}")
end
if old.equal?(replacement)
self
elsif @path_from_root != nil
new_path = self.class.replace(@path_from_root, old, replacement)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("replaced #{old} with #{replacement} in #{self}")
Hocon::Impl::ConfigImpl.trace("path was: #{@path_from_root} is now #{new_path}")
end
# if we end up nuking the root object itself, we replace it with an
# empty root
if new_path != nil
return self.class.new(new_path.last, new_path)
else
return self.class.new(Hocon::Impl::SimpleConfigObject.empty)
end
else
if old.equal?(@root)
return self.class.new(root_must_be_obj(replacement))
else
raise ConfigBugOrBrokenError.new("attempt to replace root #{root} with #{replacement}")
end
end
end
# replacement may be null to delete
def replace_within_current_parent(old, replacement)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("replaceWithinCurrentParent old #{old}@#{old.hash}" +
" replacement #{replacement}@#{old.hash} in #{self}")
end
if old.equal?(replacement)
self
elsif @path_from_root != nil
parent = @path_from_root.head
new_parent = parent.replace_child(old, replacement)
return replace_current_parent(parent, new_parent.is_a?(Hocon::Impl::Container) ? new_parent : nil)
else
if old.equal?(@root) && replacement.is_a?(Hocon::Impl::Container)
return self.class.new(root_must_be_obj(replacement))
else
raise ConfigBugOrBrokenError.new("replace in parent not possible #{old} with #{replacement}" +
" in #{self}")
end
end
end
def to_s
"ResolveSource(root=#{@root}, pathFromRoot=#{@path_from_root})"
end
# a persistent list
class Node
attr_reader :next_node, :value
def initialize(value, next_node = nil)
@value = value
@next_node = next_node
end
def prepend(value)
Node.new(value, self)
end
def head
@value
end
def tail
@next_node
end
def last
i = self
while i.next_node != nil
i = i.next_node
end
i.value
end
def reverse
if @next_node == nil
self
else
reversed = Node.new(@value)
i = @next_node
while i != nil
reversed = reversed.prepend(i.value)
i = i.next_node
end
reversed
end
end
def to_s
sb = ""
sb << "["
to_append_value = self.reverse
while to_append_value != nil
sb << to_append_value.value.to_s
if to_append_value.next_node != nil
sb << " <= "
end
to_append_value = to_append_value.next_node
end
sb << "]"
sb
end
end
# value is allowed to be null
class ValueWithPath
attr_reader :value, :path_from_root
def initialize(value, path_from_root)
@value = value
@path_from_root = path_from_root
end
def to_s
"ValueWithPath(value=" + @value + ", pathFromRoot=" + @path_from_root + ")"
end
end
class ResultWithPath
attr_reader :result, :path_from_root
def initialize(result, path_from_root)
@result = result
@path_from_root = path_from_root
end
def to_s
"ResultWithPath(result=#{@result}, pathFromRoot=#{@path_from_root})"
end
end
private
def root_must_be_obj(value)
if value.is_a?(Hocon::Impl::AbstractConfigObject)
value
else
Hocon::Impl::SimpleConfigObject.empty
end
end
def self.find_in_object_impl(obj, path, parents = nil)
begin
# we 'll fail if anything along the path can' t
# be looked at without resolving.
find_in_object_impl_impl(obj, path, nil)
rescue ConfigNotResolvedError => e
raise Hocon::Impl::ConfigImpl.improve_not_resolved(path, e)
end
end
def self.find_in_object_impl_impl(obj, path, parents)
key = path.first
remainder = path.remainder
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("*** looking up '#{key}' in #{obj}")
end
v = obj.attempt_peek_with_partial_resolve(key)
new_parents = parents == nil ? Node.new(obj) : parents.prepend(obj)
if remainder == nil
ValueWithPath.new(v, new_parents)
else
if v.is_a?(Hocon::Impl::AbstractConfigObject)
find_in_object_impl_impl(v, remainder, new_parents)
else
ValueWithPath.new(nil, new_parents)
end
end
end
# returns null if the replacement results in deleting all the nodes.
def self.replace(list, old, replacement)
child = list.head
unless child.equal?(old)
raise ConfigBugOrBrokenError.new("Can only replace() the top node we're resolving; had " + child +
" on top and tried to replace " + old + " overall list was " + list)
end
parent = list.tail == nil ? nil : list.tail.head
if replacement == nil || !replacement.is_a?(Hocon::Impl::Container)
if parent == nil
return nil
else
# we are deleting the child from the stack of containers
# because it's either going away or not a container
new_parent = parent.replace_child(old, nil)
return replace(list.tail, parent, new_parent)
end
else
# we replaced the container with another container
if parent == nil
return Node.new(replacement)
else
new_parent = parent.replace_child(old, replacement)
new_tail = replace(list.tail, parent, new_parent)
if new_tail != nil
return new_tail.prepend(replacement)
else
return Node.new(replacement)
end
end
end
end
end
hocon-1.3.1/lib/hocon/impl/config_node_include.rb 0000644 0000041 0000041 00000001226 13704151031 021765 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/config_error'
require 'hocon/impl/abstract_config_node'
require 'hocon/impl/config_node_simple_value'
class Hocon::Impl::ConfigNodeInclude
include Hocon::Impl::AbstractConfigNode
def initialize(children, kind)
@children = children
@kind = kind
end
attr_reader :kind, :children
def tokens
tokens = []
@children.each do |child|
tokens += child.tokens
end
tokens
end
def name
@children.each do |child|
if child.is_a?(Hocon::Impl::ConfigNodeSimpleValue)
return Hocon::Impl::Tokens.value(child.token).unwrapped
end
end
nil
end
end hocon-1.3.1/lib/hocon/impl/substitution_expression.rb 0000644 0000041 0000041 00000001224 13704151031 023041 0 ustar www-data www-data require 'hocon/impl'
class Hocon::Impl::SubstitutionExpression
def initialize(path, optional)
@path = path
@optional = optional
end
attr_reader :path, :optional
def change_path(new_path)
if new_path == @path
self
else
Hocon::Impl::SubstitutionExpression.new(new_path, @optional)
end
end
def to_s
"${#{@optional ? "?" : ""}#{@path.render}}"
end
def ==(other)
if other.is_a? Hocon::Impl::SubstitutionExpression
other.path == @path && other.optional == @optional
else
false
end
end
def hash
h = 41 * (41 + @path.hash)
h = 41 * (h + (optional ? 1 : 0))
h
end
end hocon-1.3.1/lib/hocon/impl/abstract_config_value.rb 0000644 0000041 0000041 00000026215 13704151031 022341 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'stringio'
require 'hocon/config_render_options'
require 'hocon/config_object'
require 'hocon/impl/resolve_status'
require 'hocon/impl/resolve_result'
require 'hocon/impl/unmergeable'
require 'hocon/impl/config_impl_util'
require 'hocon/config_error'
require 'hocon/config_value'
##
## 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().
##
module Hocon::Impl::AbstractConfigValue
include Hocon::ConfigValue
ConfigImplUtil = Hocon::Impl::ConfigImplUtil
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
ResolveStatus = Hocon::Impl::ResolveStatus
def initialize(origin)
@origin = origin
end
attr_reader :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().
class NotPossibleToResolve < Exception
def initialize(context)
super("was not possible to resolve")
@trace_string = context.trace_string
end
attr_reader :trace_string
end
# 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
def resolve_substitutions(context, source)
Hocon::Impl::ResolveResult.make(context, self)
end
def resolve_status
Hocon::Impl::ResolveStatus::RESOLVED
end
def self.replace_child_in_list(list, child, replacement)
i = 0
while (i < list.size) && (! list[i].equal?(child))
i += 1
end
if (i == list.size)
raise ConfigBugOrBrokenError, "tried to replace #{child} which is not in #{list}"
end
new_stack = list.clone
if ! replacement.nil?
new_stack[i] = replacement
else
new_stack.delete(i)
end
if new_stack.empty?
nil
else
new_stack
end
end
def self.has_descendant_in_list?(list, descendant)
list.each do |v|
if v.equal?(descendant)
return true
end
end
# now the expensive traversal
list.each do |v|
if v.is_a?(Hocon::Impl::Container) && v.has_descendant?(descendant)
return true
end
end
false
end
# 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
def relativized(prefix)
self
end
module NoExceptionsModifier
def modify_child_may_throw(key_or_nil, v)
begin
modify_child(key_or_nil, v)
rescue Hocon::ConfigError => e
raise e
end
end
end
def to_fallback_value
self
end
def new_copy(origin)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigValue should provide their own implementation of `new_copy` (#{self.class})"
end
# 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.
def ignores_fallbacks?
# if we are not resolved, then somewhere in this value there's
# a substitution that may need to look at the fallbacks.
resolve_status == Hocon::Impl::ResolveStatus::RESOLVED
end
def with_fallbacks_ignored
if ignores_fallbacks?
self
else
raise ConfigBugOrBrokenError, "value class doesn't implement forced fallback-ignoring #{self}"
end
end
# the withFallback() implementation is supposed to avoid calling
# mergedWith* if we're ignoring fallbacks.
def require_not_ignoring_fallbacks
if ignores_fallbacks?
raise ConfigBugOrBrokenError, "method should not have been called with ignoresFallbacks=true #{self.class.name}"
end
end
def construct_delayed_merge(origin, stack)
# TODO: this might not work because ConfigDelayedMerge inherits
# from this class, so we can't `require` it from this file
require 'hocon/impl/config_delayed_merge'
Hocon::Impl::ConfigDelayedMerge.new(origin, stack)
end
def merged_stack_with_the_unmergeable(stack, fallback)
require_not_ignoring_fallbacks
# if we turn out to be an object, and the fallback also does,
# then a merge may be required; delay until we resolve.
new_stack = stack.clone
new_stack.concat(fallback.unmerged_values)
# TODO: this might not work because AbstractConfigObject inherits
# from this class, so we can't `require` it from this file
construct_delayed_merge(Hocon::Impl::AbstractConfigObject.merge_origins(new_stack), new_stack)
end
def delay_merge(stack, 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
new_stack = stack.clone
new_stack << fallback
# TODO: this might not work because AbstractConfigObject inherits
# from this class, so we can't `require` it from this file
construct_delayed_merge(Hocon::Impl::AbstractConfigObject.merge_origins(new_stack), new_stack)
end
def merged_stack_with_object(stack, fallback)
require_not_ignoring_fallbacks
# TODO: this might not work because AbstractConfigObject inherits
# from this class, so we can't `require` it from this file
if self.is_a?(Hocon::Impl::AbstractConfigObject)
raise ConfigBugOrBrokenError, "Objects must reimplement merged_with_object"
end
merged_stack_with_non_object(stack, fallback)
end
def merged_stack_with_non_object(stack, fallback)
require_not_ignoring_fallbacks
if resolve_status == 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.
with_fallbacks_ignored
else
# if unresolved we may have to look back to fallbacks as part of
# the resolution process, so always delay
delay_merge(stack, fallback)
end
end
def merged_with_the_unmergeable(fallback)
require_not_ignoring_fallbacks
merged_stack_with_the_unmergeable([self], fallback)
end
def merged_with_object(fallback)
require_not_ignoring_fallbacks
merged_stack_with_object([self], fallback)
end
def merged_with_non_object(fallback)
require_not_ignoring_fallbacks
merged_stack_with_non_object([self], fallback)
end
def with_origin(origin)
if @origin.equal?(origin)
self
else
new_copy(origin)
end
end
def with_fallback(mergeable)
if ignores_fallbacks?
self
else
other = mergeable.to_fallback_value
if other.is_a?(Hocon::Impl::Unmergeable)
merged_with_the_unmergeable(other)
# TODO: this probably isn't going to work because AbstractConfigObject inherits
# from this class, so we can't `require` it from this file
elsif other.is_a?(Hocon::Impl::AbstractConfigObject)
merged_with_object(other)
else
merged_with_non_object(other)
end
end
end
def can_equal(other)
other.is_a?(Hocon::Impl::AbstractConfigValue)
end
def ==(other)
# note that "origin" is deliberately NOT part of equality
if other.is_a?(Hocon::Impl::AbstractConfigValue)
can_equal(other) &&
value_type == other.value_type &&
ConfigImplUtil.equals_handling_nil?(unwrapped, other.unwrapped)
else
false
end
end
def hash
# note that "origin" is deliberately NOT part of equality
unwrapped_value = unwrapped
if unwrapped_value.nil?
0
else
unwrapped_value.hash
end
end
def to_s
sb = StringIO.new
render_to_sb(sb, 0, true, nil, Hocon::ConfigRenderOptions.concise)
"#{self.class.name.split('::').last}(#{sb.string})"
end
def inspect
to_s
end
def self.indent(sb, indent_size, options)
if options.formatted?
remaining = indent_size
while remaining > 0
sb << " "
remaining -= 1
end
end
end
def render_to_sb(sb, indent, at_root, at_key, options)
if !at_key.nil?
rendered_key =
if options.json?
ConfigImplUtil.render_json_string(at_key)
else
ConfigImplUtil.render_string_unquoted_if_possible(at_key)
end
sb << rendered_key
if options.json?
if options.formatted?
sb << ": "
else
sb << ":"
end
else
case options.key_value_separator
when :colon
sb << ": "
else
sb << "="
end end
end
render_value_to_sb(sb, indent, at_root, options)
end
# to be overridden by subclasses
def render_value_to_sb(sb, indent, at_root, options)
u = unwrapped
sb << u.to_s
end
def render(options = Hocon::ConfigRenderOptions.defaults)
sb = StringIO.new
render_to_sb(sb, 0, true, nil, options)
# We take a substring that ends at sb.pos, because we've been decrementing
# sb.pos at various points in the code as a means to remove characters from
# the end of the StringIO
sb.string[0, sb.pos]
end
# 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
# which are auto-converted to strings when concatenating with
# other strings or by the DefaultTransformer.
def transform_to_string
nil
end
def at_key(key)
at_key_with_origin(Hocon::Impl::SimpleConfigOrigin.new_simple("at_key(#{key})"), key)
end
# Renamed this to be consistent with the other at_key* overloaded methods
def at_key_with_origin(origin, key)
m = {key=>self}
Hocon::Impl::SimpleConfigObject.new(origin, m).to_config
end
# In java this is an overloaded version of atPath
def at_path_with_origin(origin, path)
parent = path.parent
result = at_key_with_origin(origin, path.last)
while not parent.nil? do
key = parent.last
result = result.at_key_with_origin(origin, key)
parent = parent.parent
end
result
end
def at_path(path_expression)
origin = Hocon::Impl::SimpleConfigOrigin.new_simple("at_path(#{path_expression})")
at_path_with_origin(origin, Hocon::Impl::Path.new_path(path_expression))
end
end
hocon-1.3.1/lib/hocon/impl/abstract_config_node_value.rb 0000644 0000041 0000041 00000000631 13704151031 023340 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/abstract_config_node'
# This essentially exists in the upstream so we can ensure only certain types of
# config nodes can be passed into some methods. That's not a problem in Ruby, so this is
# unnecessary, but it seems best to keep it around for consistency
module Hocon::Impl::AbstractConfigNodeValue
include Hocon::Impl::AbstractConfigNode
end hocon-1.3.1/lib/hocon/impl/simple_config_list.rb 0000644 0000041 0000041 00000020247 13704151031 021665 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/resolve_status'
require 'hocon/config_value_type'
require 'hocon/config_error'
require 'hocon/impl/abstract_config_object'
require 'forwardable'
require 'hocon/impl/unsupported_operation_error'
require 'hocon/impl/resolve_result'
require 'hocon/impl/container'
require 'hocon/config_list'
class Hocon::Impl::SimpleConfigList
include Hocon::Impl::Container
include Hocon::ConfigList
include Hocon::Impl::AbstractConfigValue
extend Forwardable
ResolveStatus = Hocon::Impl::ResolveStatus
ResolveResult = Hocon::Impl::ResolveResult
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
def initialize(origin, value, status = ResolveStatus.from_values(value))
super(origin)
@value = value
@resolved = (status == ResolveStatus::RESOLVED)
# kind of an expensive debug check (makes this constructor pointless)
if status != ResolveStatus.from_values(value)
raise ConfigBugOrBrokenError, "SimpleConfigList created with wrong resolve status: #{self}"
end
end
attr_reader :value
def_delegators :@value, :[], :include?, :empty?, :size, :index, :rindex, :each, :map
def value_type
Hocon::ConfigValueType::LIST
end
def unwrapped
@value.map { |v| v.unwrapped }
end
def resolve_status
ResolveStatus.from_boolean(@resolved)
end
def replace_child(child, replacement)
new_list = replace_child_in_list(@value, child, replacement)
if new_list.nil?
nil
else
# we use the constructor flavor that will recompute the resolve status
SimpleConfigList.new(origin, new_list)
end
end
def has_descendant?(descendant)
Hocon::Impl::AbstractConfigValue.has_descendant_in_list?(@value, descendant)
end
def modify(modifier, new_resolve_status)
begin
modify_may_throw(modifier, new_resolve_status)
rescue Hocon::ConfigError => e
raise e
end
end
def modify_may_throw(modifier, new_resolve_status)
# lazy-create for optimization
changed = nil
i = 0
@value.each { |v|
modified = modifier.modify_child_may_throw(nil, v)
# lazy-create the new list if required
if changed == nil && !modified.equal?(v)
changed = []
j = 0
while j < i
changed << @value[j]
j += 1
end
end
# once the new list is created, all elements
# have to go in it.if modifyChild returned
# null, we drop that element.
if changed != nil && modified != nil
changed << modified
end
i += 1
}
if changed != nil
if new_resolve_status != nil
self.class.new(origin, changed, new_resolve_status)
else
self.class.new(origin, changed)
end
else
self
end
end
class ResolveModifier
attr_reader :context, :source
def initialize(context, source)
@context = context
@source = source
end
def modify_child_may_throw(key, v)
result = @context.resolve(v, source)
@context = result.context
result.value
end
end
def resolve_substitutions(context, source)
if @resolved
return Hocon::Impl::ResolveResult.make(context, self)
end
if context.is_restricted_to_child
# if a list restricts to a child path, then it has no child paths,
# so nothing to do.
Hocon::Impl::ResolveResult.make(context, self)
else
begin
modifier = ResolveModifier.new(context, source.push_parent(self))
value = modify_may_throw(modifier, context.options.allow_unresolved ? nil : ResolveStatus::RESOLVED)
Hocon::Impl::ResolveResult.make(modifier.context, value)
rescue NotPossibleToResolve => e
raise e
rescue RuntimeError => e
raise e
rescue Exception => e
raise ConfigBugOrBrokenError.new("unexpected exception", e)
end
end
end
def relativized(prefix)
modifier = Class.new do
include Hocon::Impl::AbstractConfigValue::NoExceptionsModifier
# prefix isn't in scope inside of a def, but it is in scope inside of Class.new
# so manually define a method that has access to prefix
# I feel dirty
define_method(:modify_child) do |key, v|
v.relativized(prefix)
end
end
modify(modifier.new, resolve_status)
end
def can_equal(other)
other.is_a?(self.class)
end
def ==(other)
# note that "origin" is deliberately NOT part of equality
if other.is_a?(self.class)
# optimization to avoid unwrapped() for two ConfigList
can_equal(other) &&
(value.equal?(other.value) || (value == other.value))
else
false
end
end
def hash
# note that "origin" is deliberately NOT part of equality
value.hash
end
def render_value_to_sb(sb, indent_size, at_root, options)
if @value.empty?
sb << "[]"
else
sb << "["
if options.formatted?
sb << "\n"
end
@value.each do |v|
if options.origin_comments?
lines = v.origin.description.split("\n")
lines.each do |l|
Hocon::Impl::AbstractConfigValue.indent(sb, indent_size + 1, options)
sb << "# "
sb << l
sb << "\n"
end
end
if options.comments?
v.origin.comments.each do |comment|
sb << "# "
sb << comment
sb << "\n"
end
end
Hocon::Impl::AbstractConfigValue.indent(sb, indent_size + 1, options)
v.render_value_to_sb(sb, indent_size + 1, at_root, options)
sb << ","
if options.formatted?
sb << "\n"
end
end
# couldn't figure out a better way to chop characters off of the end of
# the StringIO. This relies on making sure that, prior to returning the
# final string, we take a substring that ends at sb.pos.
sb.pos = sb.pos - 1 # chop or newline
if options.formatted?
sb.pos = sb.pos - 1 # also chop comma
sb << "\n"
Hocon::Impl::AbstractConfigValue.indent(sb, indent_size, options)
end
sb << "]"
end
end
def contains?(o)
value.include?(o)
end
def include_all?(value_list)
value_list.all? { |v| @value.include?(v)}
end
def contains_all?(c)
include_all?(c)
end
def get(index)
value[index]
end
def index_of(o)
value.index(o)
end
def is_empty
empty?
end
# Skipping upstream definition of "iterator", because that's not really a thing
# in Ruby.
def last_index_of(o)
value.rindex(o)
end
# skipping upstream definitions of "wrapListIterator", "listIterator", and
# "listIterator(int)", because those don't really apply in Ruby.
def sub_list(from_index, to_index)
value[from_index..to_index]
end
def to_array
value
end
def we_are_immutable(method)
Hocon::Impl::UnsupportedOperationError.new("ConfigList is immutable, you can't call List. '#{method}'")
end
def add(e)
raise we_are_immutable("add")
end
def add_at(index, element)
raise we_are_immutable("add_at")
end
def add_all(c)
raise we_are_immutable("add_all")
end
def add_all_at(index, c)
raise we_are_immutable("add_all_at")
end
def clear
raise we_are_immutable("clear")
end
def remove(o)
raise we_are_immutable("remove")
end
def remove_at(i)
raise we_are_immutable("remove_at")
end
def delete(o)
raise we_are_immutable("delete")
end
def remove_all(c)
raise we_are_immutable("remove_all")
end
def retain_all(c)
raise we_are_immutable("retain_all")
end
def set(index, element)
raise we_are_immutable("set")
end
def []=(index, element)
raise we_are_immutable("[]=")
end
def push(e)
raise we_are_immutable("push")
end
def <<(e)
raise we_are_immutable("<<")
end
def new_copy(origin)
Hocon::Impl::SimpleConfigList.new(origin, @value)
end
def concatenate(other)
combined_origin = Hocon::Impl::SimpleConfigOrigin.merge_two_origins(origin, other.origin)
combined = value + other.value
Hocon::Impl::SimpleConfigList.new(combined_origin, combined)
end
# Skipping upstream "writeReplace" until we see that we need it for something
def with_origin(origin)
super(origin)
end
end
hocon-1.3.1/lib/hocon/impl/tokenizer.rb 0000644 0000041 0000041 00000042462 13704151031 020031 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/config_impl_util'
require 'hocon/impl/tokens'
require 'hocon/config_error'
require 'stringio'
require 'forwardable'
class Hocon::Impl::Tokenizer
Tokens = Hocon::Impl::Tokens
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
class TokenizerProblemError < StandardError
def initialize(problem)
@problem = problem
end
def problem
@problem
end
end
def self.as_string(codepoint)
if codepoint == "\n"
"newline"
elsif codepoint == "\t"
"tab"
elsif codepoint == -1
"end of file"
elsif codepoint =~ /[[:cntrl:]]/
"control character 0x%x" % codepoint
else
"%c" % codepoint
end
end
# Tokenizes a Reader. Does not close the reader; you have to arrange to do
# that after you're done with the returned iterator.
def self.tokenize(origin, input, syntax)
TokenIterator.new(origin, input, syntax != Hocon::ConfigSyntax::JSON)
end
def self.render(tokens)
rendered_text = ""
while (t = tokens.next)
rendered_text << t.token_text
end
rendered_text
end
class TokenIterator
class WhitespaceSaver
def initialize
@whitespace = StringIO.new
@last_token_was_simple_value = false
end
def add(c)
@whitespace << c
end
def check(t, base_origin, line_number)
if TokenIterator.simple_value?(t)
next_is_a_simple_value(base_origin, line_number)
else
next_is_not_a_simple_value(base_origin, line_number)
end
end
private
# called if the next token is not a simple value;
# discards any whitespace we were saving between
# simple values.
def next_is_not_a_simple_value(base_origin, line_number)
@last_token_was_simple_value = false
create_whitespace_token_from_saver(base_origin, line_number)
end
# called if the next token IS a simple value,
# so creates a whitespace token if the previous
# token also was.
def next_is_a_simple_value(base_origin, line_number)
t = create_whitespace_token_from_saver(base_origin, line_number)
@last_token_was_simple_value = true unless @last_token_was_simple_value
t
end
def create_whitespace_token_from_saver(base_origin, line_number)
return nil unless @whitespace.length > 0
if (@last_token_was_simple_value)
t = Tokens.new_unquoted_text(
Hocon::Impl::Tokenizer::TokenIterator.line_origin(base_origin, line_number),
String.new(@whitespace.string)
)
else
t = Tokens.new_ignored_whitespace(
Hocon::Impl::Tokenizer::TokenIterator.line_origin(base_origin, line_number),
String.new(@whitespace.string)
)
end
@whitespace.string = ""
t
end
end
def initialize(origin, input, allow_comments)
@origin = origin
@input = input
@allow_comments = allow_comments
@buffer = []
@line_number = 1
@line_origin = @origin.with_line_number(@line_number)
@tokens = []
@tokens << Tokens::START
@whitespace_saver = WhitespaceSaver.new
end
# 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().
def next_char_raw
if @buffer.empty?
begin
@input.readchar.chr
rescue EOFError
-1
end
else
@buffer.pop
end
end
def put_back(c)
if @buffer.length > 2
raise ConfigBugOrBrokenError, "bug: putBack() three times, undesirable look-ahead"
end
@buffer.push(c)
end
def self.whitespace?(c)
Hocon::Impl::ConfigImplUtil.whitespace?(c)
end
def self.whitespace_not_newline?(c)
(c != "\n") and (Hocon::Impl::ConfigImplUtil.whitespace?(c))
end
def start_of_comment?(c)
if c == -1
false
else
if @allow_comments
if c == '#'
true
elsif c == '/'
maybe_second_slash = next_char_raw
# we want to predictably NOT consume any chars
put_back(maybe_second_slash)
if maybe_second_slash == '/'
true
else
false
end
end
else
false
end
end
end
# get next char, skipping non-newline whitespace
def next_char_after_whitespace(saver)
while true
c = next_char_raw
if c == -1
return -1
else
if self.class.whitespace_not_newline?(c)
saver.add(c)
else
return c
end
end
end
end
def self.problem(origin, what, message, suggest_quotes, cause)
if what.nil? || message.nil?
raise ConfigBugOrBrokenError.new("internal error, creating bad TokenizerProblemError")
end
TokenizerProblemError.new(Tokens.new_problem(origin, what, message, suggest_quotes, cause))
end
def self.line_origin(base_origin, line_number)
base_origin.with_line_number(line_number)
end
# ONE char has always been consumed, either the # or the first /, but not
# both slashes
def pull_comment(first_char)
double_slash = false
if first_char == '/'
discard = next_char_raw
if discard != '/'
raise ConfigBugOrBrokenError, "called pullComment but // not seen"
end
double_slash = true
end
io = StringIO.new
while true
c = next_char_raw
if (c == -1) || (c == "\n")
put_back(c)
if (double_slash)
return Tokens.new_comment_double_slash(@line_origin, io.string)
else
return Tokens.new_comment_hash(@line_origin, io.string)
end
else
io << c
end
end
end
# chars JSON allows a number to start with
FIRST_NUMBER_CHARS = "0123456789-"
# chars JSON allows to be part of a number
NUMBER_CHARS = "0123456789eE+-."
# chars that stop an unquoted string
NOT_IN_UNQUOTED_TEXT = "$\"{}[]:=,+#`^?!@*&\\"
# 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.
def pull_unquoted_text
origin = @line_origin
io = StringIO.new
c = next_char_raw
while true
if (c == -1) or
(NOT_IN_UNQUOTED_TEXT.index(c)) or
(self.class.whitespace?(c)) or
(start_of_comment?(c))
break
else
io << c
end
# 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 io.length == 4
if io.string == "true"
return Tokens.new_boolean(origin, true)
elsif io.string == "null"
return Tokens.new_null(origin)
end
elsif io.length == 5
if io.string == "false"
return Tokens.new_boolean(origin, false)
end
end
c = next_char_raw
end
# put back the char that ended the unquoted text
put_back(c)
Tokens.new_unquoted_text(origin, io.string)
end
def pull_number(first_char)
sb = StringIO.new
sb << first_char
contained_decimal_or_e = false
c = next_char_raw
while (c != -1) && (NUMBER_CHARS.index(c))
if (c == '.') ||
(c == 'e') ||
(c == 'E')
contained_decimal_or_e = true
end
sb << c
c = next_char_raw
end
# the last character we looked at wasn't part of the number, put it
# back
put_back(c)
s = sb.string
begin
if contained_decimal_or_e
# force floating point representation
Tokens.new_double(@line_origin, Float(s), s)
else
Tokens.new_long(@line_origin, Integer(s), s)
end
rescue ArgumentError => e
if e.message =~ /^invalid value for (Float|Integer)\(\)/
# not a number after all, see if it's an unquoted string.
s.each_char do |u|
if NOT_IN_UNQUOTED_TEXT.index(u)
raise self.class.problem(@line_origin, u, "Reserved character '#{u}'" +
"is not allowed outside quotes", true, nil)
end
end
# no evil chars so we just decide this was a string and
# not a number.
Tokens.new_unquoted_text(@line_origin, s)
else
raise e
end
end
end
def pull_escape_sequence(sb, sb_orig)
escaped = next_char_raw
if escaped == -1
error_msg = "End of input but backslash in string had nothing after it"
raise self.class.problem(@line_origin, "", error_msg, false, nil)
end
# This is needed so we return the unescaped escape characters back out when rendering
# the token
sb_orig << "\\" << escaped
case escaped
when "\""
sb << "\""
when "\\"
sb << "\\"
when "/"
sb << "/"
when "b"
sb << "\b"
when "f"
sb << "\f"
when "n"
sb << "\n"
when "r"
sb << "\r"
when "t"
sb << "\t"
when "u"
codepoint = ""
# Grab the 4 hex chars for the unicode character
4.times do
c = next_char_raw
if c == -1
error_msg = "End of input but expecting 4 hex digits for \\uXXXX escape"
raise self.class.problem(@line_origin, c, error_msg, false, nil)
end
codepoint << c
end
sb_orig << codepoint
# Convert codepoint to a unicode character
packed = [codepoint.hex].pack("U")
if packed == "_"
raise self.class.problem(@line_origin, codepoint,
"Malformed hex digits after \\u escape in string: '#{codepoint}'",
false, nil)
end
sb << packed
else
error_msg = "backslash followed by '#{escaped}', this is not a valid escape sequence (quoted strings use JSON escaping, so use double-backslash \\ for literal backslash)"
raise self.class.problem(Hocon::Impl::Tokenizer.as_string(escaped), "", error_msg, false, nil)
end
end
def append_triple_quoted_string(sb, sb_orig)
# we are after the opening triple quote and need to consume the
# close triple
consecutive_quotes = 0
while true
c = next_char_raw
if c == '"'
consecutive_quotes += 1
elsif consecutive_quotes >= 3
# the last three quotes end the string and the other kept.
sb.string = sb.string[0...-3]
put_back c
break
else
consecutive_quotes = 0
if c == -1
error_msg = "End of input but triple-quoted string was still open"
raise self.class.problem(@line_origin, c, error_msg, false, nil)
elsif c == "\n"
# keep the line number accurate
@line_number += 1
@line_origin = @origin.with_line_number(@line_number)
end
end
sb << c
sb_orig << c
end
end
def pull_quoted_string
# the open quote has already been consumed
sb = StringIO.new
# We need a second StringIO 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 StringIO to escape escape characters
# so we can also keep the actual value of the string. This is gross.
sb_orig = StringIO.new
sb_orig << '"'
c = ""
while c != '"'
c = next_char_raw
if c == -1
raise self.class.problem(@line_origin, c, "End of input but string quote was still open", false, nil)
end
if c == "\\"
pull_escape_sequence(sb, sb_orig)
elsif c == '"'
sb_orig << c
# done!
elsif c =~ /[[:cntrl:]]/
raise self.class.problem(@line_origin, c, "JSON does not allow unescaped #{c}" +
" in quoted strings, use a backslash escape", false, nil)
else
sb << c
sb_orig << c
end
end
# maybe switch to triple-quoted string, sort of hacky...
if sb.length == 0
third = next_char_raw
if third == '"'
sb_orig << third
append_triple_quoted_string(sb, sb_orig)
else
put_back(third)
end
end
Tokens.new_string(@line_origin, sb.string, sb_orig.string)
end
def pull_plus_equals
# the initial '+' has already been consumed
c = next_char_raw
unless c == '='
error_msg = "'+' not followed by =, '#{c}' not allowed after '+'"
raise self.class.problem(@line_origin, c, error_msg, true, nil) # true = suggest quotes
end
Tokens::PLUS_EQUALS
end
def pull_substitution
# the initial '$' has already been consumed
c = next_char_raw
if c != '{'
error_msg = "'$' not followed by {, '#{c}' not allowed after '$'"
raise self.class.problem(@line_origin, c, error_msg, true, nil) # true = suggest quotes
end
optional = false
c = next_char_raw
if c == '?'
optional = true
else
put_back(c)
end
saver = WhitespaceSaver.new
expression = []
while true
t = pull_next_token(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 == Tokens::CLOSE_CURLY
# end the loop, done!
break
elsif t == Tokens::EOF
raise self.class.problem(@line_origin, t, "Substitution ${ was not closed with a }", false, nil)
else
whitespace = saver.check(t, @line_origin, @line_number)
unless whitespace.nil?
expression << whitespace
end
expression << t
end
end
Tokens.new_substitution(@line_origin, optional, expression)
end
def pull_next_token(saver)
c = next_char_after_whitespace(saver)
if c == -1
Tokens::EOF
elsif c == "\n"
# newline tokens have the just-ended line number
line = Tokens.new_line(@line_origin)
@line_number += 1
@line_origin = @origin.with_line_number(@line_number)
line
else
t = nil
if start_of_comment?(c)
t = pull_comment(c)
else
t = case c
when '"' then pull_quoted_string
when '$' then pull_substitution
when ':' then Tokens::COLON
when ',' then Tokens::COMMA
when '=' then Tokens::EQUALS
when '{' then Tokens::OPEN_CURLY
when '}' then Tokens::CLOSE_CURLY
when '[' then Tokens::OPEN_SQUARE
when ']' then Tokens::CLOSE_SQUARE
when '+' then pull_plus_equals
else nil
end
if t.nil?
if FIRST_NUMBER_CHARS.index(c)
t = pull_number(c)
elsif NOT_IN_UNQUOTED_TEXT.index(c)
raise self.class.problem(@line_origin, c, "Reserved character '#{c}' is not allowed outside quotes", true, nil)
else
put_back(c)
t = pull_unquoted_text
end
end
end
if t.nil?
raise ConfigBugOrBrokenError, "bug: failed to generate next token"
end
t
end
end
def self.simple_value?(t)
Tokens.substitution?(t) ||
Tokens.unquoted_text?(t) ||
Tokens.value?(t)
end
def queue_next_token
t = pull_next_token(@whitespace_saver)
whitespace = @whitespace_saver.check(t, @origin, @line_number)
if whitespace
@tokens.push(whitespace)
end
@tokens.push(t)
end
def has_next?
!@tokens.empty?
end
def next
t = @tokens.shift
if (@tokens.empty?) and (t != Tokens::EOF)
begin
queue_next_token
rescue TokenizerProblemError => e
@tokens.push(e.problem)
end
if @tokens.empty?
raise ConfigBugOrBrokenError, "bug: tokens queue should not be empty here"
end
end
t
end
def remove
raise ConfigBugOrBrokenError, "Does not make sense to remove items from token stream"
end
def each
while has_next?
# Have to use self.next instead of next because next is a reserved word
yield self.next
end
end
def map
token_list = []
each do |token|
# yield token to calling method, append whatever is returned from the
# map block to token_list
token_list << yield(token)
end
token_list
end
def to_list
# Return array of tokens from the iterator
self.map { |token| token }
end
end
end
hocon-1.3.1/lib/hocon/impl/config_node_array.rb 0000644 0000041 0000041 00000000342 13704151031 021456 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/config_node_complex_value'
class Hocon::Impl::ConfigNodeArray
include Hocon::Impl::ConfigNodeComplexValue
def new_node(nodes)
self.class.new(nodes)
end
end hocon-1.3.1/lib/hocon/impl/mergeable_value.rb 0000644 0000041 0000041 00000000222 13704151031 021122 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/config_mergeable'
class Hocon::Impl::MergeableValue < Hocon::ConfigMergeable
# TODO
end
hocon-1.3.1/lib/hocon/impl/memo_key.rb 0000644 0000041 0000041 00000001611 13704151031 017613 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/impl'
class Hocon::Impl::MemoKey
def initialize(value, restrict_to_child_or_nil)
@value = value
@restrict_to_child_or_nil = restrict_to_child_or_nil
end
def hash
h = @value.hash
if @restrict_to_child_or_nil != nil
h + 41 * (41 + @restrict_to_child_or_nil.hash)
else
h
end
end
def ==(other)
if other.is_a?(self.class)
o = other
if !o.value.equal?(@value)
return false
elsif o.restrict_to_child_or_nil.equals(@restrict_to_child_or_nil)
return true
elsif o.restrict_to_child_or_nil == nil || @restrict_to_child_or_nil == nil
return false
else
return o.restrict_to_child_or_nil == @restrict_to_child_or_nil
end
else
false
end
end
def to_s
"MemoKey(#{@value}@#{@value.hash},#{@restrict_to_child_or_nil})"
end
end
hocon-1.3.1/lib/hocon/impl/resolve_result.rb 0000644 0000041 0000041 00000001354 13704151031 021067 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/impl'
# value is allowed to be null
class Hocon::Impl::ResolveResult
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
attr_accessor :context, :value
def initialize(context, value)
@context = context
@value = value
end
def self.make(context, value)
self.new(context, value)
end
def as_object_result
unless @value.is_a?(Hocon::Impl::AbstractConfigObject)
raise ConfigBugOrBrokenError.new("Expecting a resolve result to be an object, but it was #{@value}")
end
self
end
def as_value_result
self
end
def pop_trace
self.class.make(@context.pop_trace, value)
end
def to_s
"ResolveResult(#{@value})"
end
end
hocon-1.3.1/lib/hocon/impl/config_impl.rb 0000644 0000041 0000041 00000021155 13704151031 020301 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/simple_includer'
require 'hocon/config_error'
require 'hocon/impl/from_map_mode'
require 'hocon/impl/simple_config_origin'
require 'hocon/impl/simple_config_list'
require 'hocon/impl/config_boolean'
require 'hocon/impl/config_null'
require 'hocon/impl/parseable'
class Hocon::Impl::ConfigImpl
@default_includer = Hocon::Impl::SimpleIncluder.new(nil)
@default_value_origin = Hocon::Impl::SimpleConfigOrigin.new_simple("hardcoded value")
@default_true_value = Hocon::Impl::ConfigBoolean.new(@default_value_origin, true)
@default_false_value = Hocon::Impl::ConfigBoolean.new(@default_value_origin, false)
@default_null_value = Hocon::Impl::ConfigNull.new(@default_value_origin)
@default_empty_list = Hocon::Impl::SimpleConfigList.new(@default_value_origin, Array.new)
@default_empty_object = Hocon::Impl::SimpleConfigObject.empty(@default_value_origin)
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError
FromMapMode = Hocon::Impl::FromMapMode
def self.default_includer
@default_includer
end
class FileNameSource < Hocon::Impl::SimpleIncluder::NameSource
def name_to_parseable(name, parse_options)
Hocon::Impl::Parseable.new_file(name, parse_options)
end
end
def self.improve_not_resolved(what, original)
new_message = "#{what.render} has not been resolved, you need to call Config#resolve, see API docs for Config#resolve"
if new_message == original.message
return original
else
return ConfigNotResolvedError.new(new_message, original)
end
end
def self.value_origin(origin_description)
if origin_description.nil?
return @default_value_origin
else
return Hocon::Impl::SimpleConfigOrigin.new_simple(origin_description)
end
end
def self.parse_file_any_syntax(basename, base_options)
source = FileNameSource.new()
Hocon::Impl::SimpleIncluder.from_basename(source,
File.expand_path(basename),
base_options)
end
def self.empty_list(origin)
if origin.nil? || origin == @default_value_origin
return @default_empty_list
else
return Hocon::Impl::SimpleConfigList.new(origin, Array.new)
end
end
def self.from_any_ref(object, origin_description)
origin = self.value_origin(origin_description)
from_any_ref_mode(object, origin, FromMapMode::KEYS_ARE_KEYS)
end
def self.from_any_ref_mode(object, origin, map_mode)
if origin.nil?
raise ConfigBugOrBrokenError.new("origin not supposed to be nil")
end
if object.nil?
if origin != @default_value_origin
return Hocon::Impl::ConfigNull.new(origin)
else
return @default_null_value
end
elsif object.is_a?(Hocon::Impl::AbstractConfigValue)
return object
elsif object.is_a?(TrueClass) || object.is_a?(FalseClass)
if origin != @default_value_origin
return Hocon::Impl::ConfigBoolean.new(origin, object)
elsif object
return @default_true_value
else
return @default_false_value
end
elsif object.is_a?(String)
return Hocon::Impl::ConfigString::Quoted.new(origin, object)
elsif object.is_a?(Numeric)
# 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.is_a?(Float)
return Hocon::Impl::ConfigDouble.new(origin, object, nil)
elsif object.is_a?(Integer)
return Hocon::Impl::ConfigInt.new(origin, object, nil)
else
return Hocon::Impl::ConfigNumber.new_number(origin, Float(object), nil)
end
elsif object.is_a?(Hash)
if object.empty?
return self.empty_object_from_origin(origin)
end
if map_mode == FromMapMode::KEYS_ARE_KEYS
values = Hash.new
object.each do |key, entry|
if not key.is_a?(String)
raise ConfigBugOrBrokenError.new(
"bug in method caller: not valid to create ConfigObject from map with non-String key: #{key}")
end
value = self.from_any_ref_mode(entry, origin, map_mode)
values[key] = value
end
return Hocon::Impl::SimpleConfigObject.new(origin, values)
else
raise ConfigBugOrBrokenError, "java properties format not supported"
end
elsif object.is_a?(Enumerable)
if object.count == 0
return self.empty_list(origin)
end
values = Array.new
object.each do |item|
v = from_any_ref_mode(item, origin, map_mode)
values.push(v)
end
return Hocon::Impl::SimpleConfigList.new(origin, values)
else
raise ConfigBugOrBrokenError.new("bug in method caller: not valid to create ConfigValue from: #{object}")
end
end
def self.env_variables_as_config_object
EnvVariablesHolder.get_env_variables
end
# This class is a lot simpler than the Java version ...
# The Java version uses system properties to toggle these settings.
# We don't have system properties in MRI so it's not clear what to do here.
# Initially, I ported this as more of a direct translation from the Java code,
# but I ran into issues around how to translate stupid Java static
# initialization crap to Ruby, so what we have here is a much simpler version
# that is # equivalent.
#
# There's no way to toggle this logging without changing code, but it's
# actually proved to be useful for debugging purposes while porting code
# down from Java.
class DebugHolder
class << self
def trace_loads_enabled
TRACE_LOADS_ENABLED
end
def trace_substitutions_enabled
TRACE_SUBSTITUTIONS_ENABLED
end
private
TRACE_LOADS_ENABLED = false
TRACE_SUBSTITUTIONS_ENABLED = false
end
end
def self.trace_loads_enabled
# Ignoring 'catch ExceptionInInitializerError' from that java version,
# that is just terrible java code anyway.
DebugHolder.trace_loads_enabled
end
def self.trace_substitution_enabled
# Ignoring 'catch ExceptionInInitializerError' from that java version,
# that is just terrible java code anyway.
DebugHolder.trace_substitutions_enabled
end
def self.trace(message, indent_level = 0)
while indent_level > 0
$stderr.putc(" ")
indent_level -= 1
end
$stderr.puts(message)
end
def self.empty_object_from_origin(origin)
# we want null origin to go to SimpleConfigObject.empty() to get the
# origin "empty config" rather than "hardcoded value"
if origin == @default_value_origin
@default_empty_object
else
Hocon::Impl::SimpleConfigObject.empty(origin)
end
end
def self.empty_object(origin_description)
if !origin_description.nil?
origin = Hocon::Impl::SimpleConfigOrigin.new_simple(origin_description)
else
origin = nil
end
empty_object_from_origin(origin)
end
def self.empty_config(origin_description)
empty_object(origin_description).to_config
end
def empty(origin)
self.class.empty_object_from_origin(origin)
end
def self.default_reference
resource = Hocon::Impl::Parseable.new_resources("reference.conf",
Hocon::ConfigParseOptions.defaults)
resource.parse.to_config
end
private
def self.load_env_variables
env = ENV
m = {}
lists = {}
env.each do |key, value|
m[key] = Hocon::Impl::ConfigString::Quoted.new(
Hocon::Impl::SimpleConfigOrigin.new_simple("env var #{key}"), value)
# find env var arrays specified like FOO.0, FOO.1
if key =~ /^(.*)\.(\d+)$/
name, index = $1, Integer($2)
# values are added unordered
(lists[name] ||= {})[index] = value
end
end
lists.each do |key, values|
origin = Hocon::Impl::SimpleConfigOrigin.new_simple("env var list #{key}")
m[key] = Hocon::Impl::SimpleConfigList.new(
origin,
# out of order env vars FOO.1, FOO.0 are sorted here
values.sort.map do |v|
Hocon::Impl::ConfigString::Quoted.new(origin, v[1])
end
)
end
Hocon::Impl::SimpleConfigObject.new(
Hocon::Impl::SimpleConfigOrigin.new_simple("env variables"),
m,
Hocon::Impl::ResolveStatus::RESOLVED,
false)
end
class EnvVariablesHolder
ENV_VARIABLES = Hocon::Impl::ConfigImpl.load_env_variables
def self.get_env_variables
ENV_VARIABLES
end
end
end
hocon-1.3.1/lib/hocon/impl/abstract_config_node.rb 0000644 0000041 0000041 00000001140 13704151031 022140 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/parser/config_node'
require 'hocon/config_error'
module Hocon::Impl::AbstractConfigNode
include Hocon::Parser::ConfigNode
def tokens
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of AbstractConfigNode should override `tokens` (#{self.class})"
end
def render
orig_text = StringIO.new
tokens.each do |t|
orig_text << t.token_text
end
orig_text.string
end
def ==(other)
other.is_a?(Hocon::Impl::AbstractConfigNode) &&
(render == other.render)
end
def hash
render.hash
end
end
hocon-1.3.1/lib/hocon/impl/config_boolean.rb 0000644 0000041 0000041 00000000746 13704151031 020762 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/abstract_config_value'
class Hocon::Impl::ConfigBoolean
include Hocon::Impl::AbstractConfigValue
def initialize(origin, value)
super(origin)
@value = value
end
attr_reader :value
def value_type
Hocon::ConfigValueType::BOOLEAN
end
def unwrapped
@value
end
def transform_to_string
@value.to_s
end
def new_copy(origin)
Hocon::Impl::ConfigBoolean.new(origin, @value)
end
end
hocon-1.3.1/lib/hocon/impl/url.rb 0000644 0000041 0000041 00000001623 13704151031 016613 0 ustar www-data www-data # encoding: utf-8
require 'uri'
require 'hocon/impl'
# There are several places in the Java codebase that
# use Java's URL constructor, and rely on it to throw
# a `MalformedURLException` if the URL isn't valid.
#
# Ruby doesn't really have a similar constructor /
# validator, so this is a little shim to hopefully
# make the ported code match up with the upstream more
# closely.
class Hocon::Impl::Url
class MalformedUrlError < StandardError
def initialize(msg, cause = nil)
super(msg)
@cause = cause
end
end
def initialize(url)
begin
# URI::parse wants a string
@url = URI.parse(url.to_s)
if !(@url.kind_of?(URI::HTTP))
raise MalformedUrlError, "Unrecognized URL: '#{url}'"
end
rescue URI::InvalidURIError => e
raise MalformedUrlError.new("Unrecognized URL: '#{url}' (error: #{e})", e)
end
end
def to_s
@url.to_s
end
end
hocon-1.3.1/lib/hocon/impl/config_number.rb 0000644 0000041 0000041 00000003207 13704151031 020626 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/abstract_config_value'
class Hocon::Impl::ConfigNumber
include Hocon::Impl::AbstractConfigValue
## sigh... requiring these subclasses before this class
## is declared would cause an error. Thanks, ruby.
require 'hocon/impl/config_int'
require 'hocon/impl/config_double'
def self.new_number(origin, number, original_text)
as_int = number.to_i
if as_int == number
Hocon::Impl::ConfigInt.new(origin, as_int, original_text)
else
Hocon::Impl::ConfigDouble.new(origin, number, original_text)
end
end
def initialize(origin, original_text)
super(origin)
@original_text = original_text
end
attr_reader :original_text
def transform_to_string
@original_text
end
def int_value_range_checked(path)
# We don't need to do any range checking here due to the way Ruby handles
# integers (doesn't have the 32-bit/64-bit distinction that Java does).
long_value
end
def long_value
raise "long_value needs to be overriden by sub-classes of #{Hocon::Impl::ConfigNumber}, in this case #{self.class}"
end
def can_equal(other)
other.is_a?(Hocon::Impl::ConfigNumber)
end
def ==(other)
if other.is_a?(Hocon::Impl::ConfigNumber) && can_equal(other)
@value == other.value
else
false
end
end
def hash
# This hash function makes it so that a ConfigNumber with a 3.0
# and one with a 3 will return the hash code
to_int = @value.round
# If the value is an integer or a floating point equal to an integer
if to_int == @value
to_int.hash
else
@value.hash
end
end
end
hocon-1.3.1/lib/hocon/impl/unmergeable.rb 0000644 0000041 0000041 00000001006 13704151031 020272 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/config_error'
#
# 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}.
#
module Hocon::Impl::Unmergeable
def unmerged_values
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Unmergeable` must implement `unmerged_values` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/impl/path_parser.rb 0000644 0000041 0000041 00000020022 13704151031 020313 0 ustar www-data www-data # encoding: utf-8
require 'stringio'
require 'hocon/impl'
require 'hocon/config_syntax'
require 'hocon/impl/tokenizer'
require 'hocon/impl/config_node_path'
require 'hocon/impl/tokens'
require 'hocon/config_value_type'
require 'hocon/config_error'
class Hocon::Impl::PathParser
ConfigSyntax = Hocon::ConfigSyntax
SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin
Tokenizer = Hocon::Impl::Tokenizer
Tokens = Hocon::Impl::Tokens
ConfigNodePath = Hocon::Impl::ConfigNodePath
ConfigValueType = Hocon::ConfigValueType
ConfigBadPathError = Hocon::ConfigError::ConfigBadPathError
class Element
def initialize(initial, can_be_empty)
@can_be_empty = can_be_empty
@sb = StringIO.new(initial)
end
attr_accessor :can_be_empty, :sb
def to_string
"Element(#{@sb.string},#{@can_be_empty})"
end
end
def self.api_origin
SimpleConfigOrigin.new_simple("path parameter")
end
def self.parse_path_node(path, flavor = ConfigSyntax::CONF)
reader = StringIO.new(path)
begin
tokens = Tokenizer.tokenize(api_origin, reader,
flavor)
tokens.next # drop START
parse_path_node_expression(tokens, api_origin, path, flavor)
ensure
reader.close
end
end
def self.parse_path(path)
speculated = speculative_fast_parse_path(path)
if not speculated.nil?
return speculated
end
reader = StringIO.new(path)
begin
tokens = Tokenizer.tokenize(api_origin, reader, ConfigSyntax::CONF)
tokens.next # drop START
return parse_path_expression(tokens, api_origin, path)
ensure
reader.close
end
end
def self.parse_path_node_expression(expression, origin, original_text = nil,
flavor = ConfigSyntax::CONF)
path_tokens = []
path = parse_path_expression(expression, origin, original_text, path_tokens, flavor)
ConfigNodePath.new(path, path_tokens);
end
def self.parse_path_expression(expression, origin, original_text = nil,
path_tokens = nil, flavor = ConfigSyntax::CONF)
# each builder in "buf" is an element in the path
buf = []
buf.push(Element.new("", false))
if !expression.has_next?
raise ConfigBadPathError.new(
origin,
original_text,
"Expecting a field name or path here, but got nothing")
end
while expression.has_next?
t = expression.next
if ! path_tokens.nil?
path_tokens << t
end
# Ignore all IgnoredWhitespace tokens
next if Tokens.ignored_whitespace?(t)
if Tokens.value_with_type?(t, ConfigValueType::STRING)
v = Tokens.value(t)
# this is a quoted string; so any periods
# in here don't count as path separators
s = v.transform_to_string
add_path_text(buf, true, s)
elsif t == Tokens::EOF
# 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 EOF.
else
# any periods outside of a quoted string count as
# separators
text = nil
if Tokens.value?(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.
v = Tokens.value(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 ! path_tokens.nil?
path_tokens.delete_at(path_tokens.size - 1)
path_tokens.concat(split_token_on_period(t, flavor))
end
text = v.transform_to_string
elsif Tokens.unquoted_text?(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 ! path_tokens.nil?
path_tokens.delete_at(path_tokens.size - 1)
path_tokens.concat(split_token_on_period(t, flavor))
end
text = Tokens.unquoted_text(t)
else
raise ConfigBadPathError.new(
origin,
original_text,
"Token not allowed in path expression: #{t}" +
" (you can double-quote this token if you really want it here)")
end
add_path_text(buf, false, text)
end
end
pb = Hocon::Impl::PathBuilder.new
buf.each do |e|
if (e.sb.length == 0) && !e.can_be_empty
raise Hocon::ConfigError::ConfigBadPathError.new(
origin,
original_text,
"path has a leading, trailing, or two adjacent period '.' (use quoted \"\" empty string if you want an empty element)")
else
pb.append_key(e.sb.string)
end
end
pb.result
end
def self.split_token_on_period(t, flavor)
token_text = t.token_text
if token_text == "."
return [t]
end
split_token = token_text.split('.')
split_tokens = []
split_token.each do |s|
if flavor == ConfigSyntax::CONF
split_tokens << Tokens.new_unquoted_text(t.origin, s)
else
split_tokens << Tokens.new_string(t.origin, s, "\"#{s}\"")
end
split_tokens << Tokens.new_unquoted_text(t.origin, ".")
end
if token_text[-1] != "."
split_tokens.delete_at(split_tokens.size - 1)
end
split_tokens
end
def self.add_path_text(buf, was_quoted, new_text)
i = if was_quoted
-1
else
new_text.index('.') || -1
end
current = buf.last
if i < 0
# add to current path element
current.sb << new_text
# any empty quoted string means this element can
# now be empty.
if was_quoted && (current.sb.length == 0)
current.can_be_empty = true
end
else
# "buf" plus up to the period is an element
current.sb << new_text[0, i]
# then start a new element
buf.push(Element.new("", false))
# recurse to consume remainder of new_text
add_path_text(buf, false, new_text[i + 1, new_text.length - 1])
end
end
# the idea is to see if the string has any chars or features
# that might require the full parser to deal with.
def self.looks_unsafe_for_fast_parser(s)
last_was_dot = true # // start of path is also a "dot"
len = s.length
if s.empty?
return true
end
if s[0] == "."
return true
end
if s[len - 1] == "."
return true
end
(0..len).each do |i|
c = s[i]
if c =~ /^\w$/
last_was_dot = false
next
elsif c == '.'
if last_was_dot
return true # ".." means we need to throw an error
end
last_was_dot = true
elsif c == '-'
if last_was_dot
return true
end
next
else
return true
end
end
if last_was_dot
return true
end
false
end
def self.fast_path_build(tail, s, path_end)
# rindex takes last index it should look at, end - 1 not end
split_at = s.rindex(".", path_end - 1)
tokens = []
tokens << Tokens.new_unquoted_text(nil, s)
# this works even if split_at is -1; then we start the substring at 0
with_one_more_element = Path.new(s[split_at + 1..path_end], tail)
if split_at < 0
with_one_more_element
else
fast_path_build(with_one_more_element, s, split_at)
end
end
# do something much faster than the full parser if
# we just have something like "foo" or "foo.bar"
def self.speculative_fast_parse_path(path)
s = Hocon::Impl::ConfigImplUtil.unicode_trim(path)
if looks_unsafe_for_fast_parser(s)
return nil
end
fast_path_build(nil, s, s.length)
end
end
hocon-1.3.1/lib/hocon/impl/replaceable_merge_stack.rb 0000644 0000041 0000041 00000001262 13704151031 022613 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/container'
require 'hocon/config_error'
#
# Implemented by a merge stack (ConfigDelayedMerge, ConfigDelayedMergeObject)
# that replaces itself during substitution resolution in order to implement
# "look backwards only" semantics.
#
module Hocon::Impl::ReplaceableMergeStack
include Hocon::Impl::Container
#
# Make a replacement for this object skipping the given number of elements
# which are lower in merge priority.
#
def make_replacement(context, skipping)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ReplaceableMergeStack` must implement `make_replacement` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/impl/parseable.rb 0000644 0000041 0000041 00000040653 13704151031 017755 0 ustar www-data www-data # encoding: utf-8
require 'stringio'
require 'pathname'
require 'hocon/impl'
require 'hocon/config_error'
require 'hocon/config_syntax'
require 'hocon/config_value_type'
require 'hocon/impl/config_impl'
require 'hocon/impl/simple_include_context'
require 'hocon/impl/simple_config_object'
require 'hocon/impl/simple_config_origin'
require 'hocon/impl/tokenizer'
require 'hocon/impl/config_parser'
require 'hocon/config_parseable'
require 'hocon/impl/config_document_parser'
require 'hocon/impl/simple_config_document'
#
# 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.
#
class Hocon::Impl::Parseable
include Hocon::ConfigParseable
# Internal implementation detail, not ABI stable, do not touch
module Relativizer
def relative_to(filename)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Relativizer` must implement `relative_to` (#{self.class})"
end
end
# The upstream library seems to use this as a global way of keeping track of
# how many files have been included, to avoid cycles
def self.parse_stack
Thread.current[:hocon_parse_stack] ||= []
end
MAX_INCLUDE_DEPTH = 50
def initialize
end
def fixup_options(base_options)
syntax = base_options.syntax
if !syntax
syntax = guess_syntax
end
if !syntax
syntax = Hocon::ConfigSyntax::CONF
end
modified = base_options.set_syntax(syntax)
# make sure the app-provided includer falls back to default
modified = modified.append_includer(Hocon::Impl::ConfigImpl.default_includer)
# make sure the app-provided includer is complete
modified = modified.set_includer(Hocon::Impl::SimpleIncluder.make_full(modified.includer))
modified
end
def post_construct(base_options)
@initial_options = fixup_options(base_options)
@include_context = Hocon::Impl::SimpleIncludeContext.new(self)
if @initial_options.origin_description
@initial_origin = Hocon::Impl::SimpleConfigOrigin.new_simple(@initial_options.origin_description)
else
@initial_origin = create_origin
end
end
# 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.
def custom_reader
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Parseable` must implement `custom_reader` (#{self.class})"
end
def reader(options)
custom_reader
end
def self.trace(message)
if Hocon::Impl::ConfigImpl.trace_loads_enabled
Hocon::Impl::ConfigImpl.trace(message)
end
end
def guess_syntax
nil
end
def content_type
nil
end
def relative_to(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
resource = filename
if filename.start_with?("/")
resource = filename.slice(1)
end
self.class.new_resources(resource, options.set_origin_description(nil))
end
def include_context
@include_context
end
def self.force_parsed_to_object(value)
if value.is_a? Hocon::Impl::AbstractConfigObject
value
else
raise Hocon::ConfigError::ConfigWrongTypeError.with_expected_actual(value.origin,
"",
"object at file root",
Hocon::ConfigValueType.value_type_name(value.value_type))
end
end
def parse(base_options = nil)
if (base_options.nil?)
base_options = options
end
stack = self.class.parse_stack
if stack.length >= MAX_INCLUDE_DEPTH
raise Hocon::ConfigError::ConfigParseError.new(@initial_origin,
"include statements nested more than #{MAX_INCLUDE_DEPTH} times, " +
"you probably have a cycle in your includes. Trace: #{stack}",
nil)
end
# Push into beginning of stack
stack.unshift(self)
begin
self.class.force_parsed_to_object(parse_value(base_options))
ensure
# Pop from beginning of stack
stack.shift
end
end
def parse_value(base_options = nil)
if base_options.nil?
base_options = options
end
# 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.
options = fixup_options(base_options)
# passed-in options can override origin
origin =
if options.origin_description
Hocon::Impl::SimpleConfigOrigin.new_simple(options.origin_description)
else
@initial_origin
end
parse_value_from_origin(origin, options)
end
def parse_value_from_origin(origin, final_options)
begin
raw_parse_value(origin, final_options)
rescue IOError => e
if final_options.allow_missing?
Hocon::Impl::SimpleConfigObject.empty_missing(origin)
else
self.class.trace("exception loading #{origin.description}: #{e.class}: #{e.message}")
raise Hocon::ConfigError::ConfigIOError.new(origin, "#{e.class.name}: #{e.message}", e)
end
end
end
def parse_document(base_options = nil)
if base_options.nil?
base_options = options
end
# 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.
options = fixup_options(base_options)
# passed-in option can override origin
origin = nil
if ! options.origin_description.nil?
origin = Hocon::Impl::SimpleConfigOrigin.new_simple(options.origin_description)
else
origin = @initial_origin
end
parse_document_from_origin(origin, options)
end
def parse_document_from_origin(origin, final_options)
begin
raw_parse_document(origin, final_options)
rescue IOError => e
if final_options.allow_missing?
Hocon::Impl::SimpleConfigDocument.new(
Hocon::Impl::ConfigNodeObject.new([]), final_options)
else
self.class.trace("exception loading #{origin.description}: #{e.class}: #{e.message}")
raise ConfigIOError.new(origin, "#{e.class.name}: #{e.message}", e)
end
end
end
# this is parseValue without post-processing the IOException or handling
# options.getAllowMissing()
def raw_parse_value(origin, final_options)
reader = reader(final_options)
# after reader() we will have loaded the Content-Type
content_type = content_type()
options_with_content_type = nil
if !(content_type.nil?)
if Hocon::Impl::ConfigImpl.trace_loads_enabled && (! final_options.get_syntax.nil?)
self.class.trace("Overriding syntax #{final_options.get_syntax} with Content-Type which specified #{content-type}")
end
options_with_content_type = final_options.set_syntax(content_type)
else
options_with_content_type = final_options
end
reader.open { |io|
raw_parse_value_from_io(io, origin, options_with_content_type)
}
end
def raw_parse_value_from_io(io, origin, final_options)
tokens = Hocon::Impl::Tokenizer.tokenize(origin, io, final_options.syntax)
document = Hocon::Impl::ConfigDocumentParser.parse(tokens, origin, final_options)
Hocon::Impl::ConfigParser.parse(document, origin, final_options, include_context)
end
def raw_parse_document(origin, final_options)
reader = reader(final_options)
content_type = content_type()
options_with_content_type = nil
if !(content_type.nil?)
if Hocon::Impl::ConfigImpl.trace_loads_enabled && (! final_options.get_syntax.nil?)
self.class.trace("Overriding syntax #{final_options.get_syntax} with Content-Type which specified #{content-type}")
end
options_with_content_type = final_options.set_syntax(content_type)
else
options_with_content_type = final_options
end
reader.open { |io|
raw_parse_document_from_io(io, origin, options_with_content_type)
}
end
def raw_parse_document_from_io(reader, origin, final_options)
tokens = Hocon::Impl::Tokenizer.tokenize(origin, reader, final_options.syntax)
Hocon::Impl::SimpleConfigDocument.new(
Hocon::Impl::ConfigDocumentParser.parse(tokens, origin, final_options),
final_options)
end
def parse_config_document
parse_document(options)
end
def origin
@initial_origin
end
def create_origin
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Parseable` must implement `create_origin` (#{self.class})"
end
def options
@initial_options
end
def to_s
self.class.name.split('::').last
end
def self.syntax_from_extension(name)
if name.end_with?(".json")
Hocon::ConfigSyntax::JSON
elsif name.end_with?(".conf")
Hocon::ConfigSyntax::CONF
else
# Skipping PROPERTIES because we can't really support that in ruby
nil
end
end
# NOTE: skipping `readerFromStream` and `doNotClose` because they don't seem relevant in Ruby
# NOTE: skipping `relativeTo(URL, String)` because we're not supporting URLs for now
def self.relative_to(file, filename)
child = Pathname.new(filename)
file = Pathname.new(file)
if child.absolute?
nil
end
parent = file.parent
if parent.nil?
nil
else
File.join(parent, filename)
end
end
# this is a parseable that doesn't exist and just throws when you try to parse it
class ParseableNotFound < Hocon::Impl::Parseable
def initialize(what, message, options)
super()
@what = what
@message = message
post_construct(options)
end
def custom_reader
raise Hocon::ConfigError::ConfigBugOrBrokenError, @message
end
def create_origin
Hocon::Impl::SimpleConfigOrigin.new_simple(@what)
end
end
def self.new_not_found(what_not_found, message, options)
ParseableNotFound.new(what_not_found, message, options)
end
# NOTE: skipping `ParseableReader` until we know we need it (probably should
# have done that with `ParseableNotFound`)
class ParseableString < Hocon::Impl::Parseable
def initialize(string, options)
super()
@input = string
post_construct(options)
end
def custom_reader
if Hocon::Impl::ConfigImpl.trace_loads_enabled
self.class.trace("Loading config from a String: #{@input}")
end
# we return self here, which will cause `open` to be called on us, so
# we can provide an implementation of that.
self
end
def open
if block_given?
StringIO.open(@input) do |f|
yield f
end
else
StringIO.open(@input)
end
end
def create_origin
Hocon::Impl::SimpleConfigOrigin.new_simple("String")
end
def to_s
"#{self.class.name.split('::').last} (#{@input})"
end
end
def self.new_string(string, options)
ParseableString.new(string, options)
end
# NOTE: Skipping `ParseableURL` for now as we probably won't support this right away
class ParseableFile < Hocon::Impl::Parseable
def initialize(input, options)
super()
@input = input
post_construct(options)
end
def custom_reader
if Hocon::Impl::ConfigImpl.trace_loads_enabled
self.class.trace("Loading config from a String: #{@input}")
end
# we return self here, which will cause `open` to be called on us, so
# we can provide an implementation of that.
self
end
def open
begin
if block_given?
File.open(@input, :encoding => 'bom|utf-8') do |f|
yield f
end
else
File.open(@input, :encoding => 'bom|utf-8')
end
rescue Errno::ENOENT
if @initial_options.allow_missing?
return Hocon::Impl::SimpleConfigObject.empty
end
raise Hocon::ConfigError::ConfigIOError.new(nil, "File not found. No file called #{@input}")
end
end
def guess_syntax
Hocon::Impl::Parseable.syntax_from_extension(File.basename(@input))
end
def relative_to(filename)
sibling = nil
if Pathname.new(filename).absolute?
sibling = File.new(filename)
else
# this may return nil
sibling = Hocon::Impl::Parseable.relative_to(@input, filename)
end
if sibling.nil?
nil
elsif File.exists?(sibling)
self.class.trace("#{sibling} exists, so loading it as a file")
Hocon::Impl::Parseable.new_file(sibling, options.set_origin_description(nil))
else
self.class.trace("#{sibling} does not exist, so trying it as a resource")
super(filename)
end
end
def create_origin
Hocon::Impl::SimpleConfigOrigin.new_file(@input)
end
def to_s
"#{self.class.name.split('::').last} (#{@input})"
end
end
def self.new_file(file_path, options)
ParseableFile.new(file_path, options)
end
# NOTE: skipping `ParseableResourceURL`, we probably won't support that
# NOTE: this is not a faithful port of the `ParseableResources` class from the
# upstream, because at least for now we're not going to try to do anything
# crazy like look for files on the ruby load path. However, there is a decent
# chunk of logic elsewhere in the codebase that is written with the assumption
# that this class will provide the 'last resort' attempt to find a config file
# before giving up, so we're basically port just enough to have it provide
# that last resort behavior
class ParseableResources < Hocon::Impl::Parseable
include Relativizer
def initialize(resource, options)
super()
@resource = resource
post_construct(options)
end
def reader
raise Hocon::ConfigError::ConfigBugOrBrokenError, "reader() should not be called on resources"
end
def raw_parse_value(origin, final_options)
# this is where the upstream code would go out and look for a file on the
# classpath. We're not going to do that, and instead we're just going to
# raise the same exception that the upstream code would raise if it failed
# to find the file.
raise IOError, "resource not found: #{@resource}"
end
def guess_syntax
Hocon::Impl::Parseable.syntax_from_extension(@resource)
end
def self.parent(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)
i = resource.rindex("/")
if i < 0
nil
else
resource.slice(0..i)
end
end
def relative_to(sibling)
if sibling.start_with?("/")
# if it starts with "/" then don't make it relative to the
# including resource
Hocon::Impl::Parseable.new_resources(sibling.slice(1), options.set_origin_description(nil))
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.
parent = self.class.parent(@resource)
if parent.nil?
Hocon::Impl::Parseable.new_resources(sibling, options.set_origin_description(nil))
else
Hocon::Impl::Parseable.new_resources("#{parent}/sibling", options.set_origin_description(nil))
end
end
end
def create_origin
Hocon::Impl::SimpleConfigOrigin.new_resource(@resource)
end
def to_s
"#{self.class.name.split('::').last}(#{@resource})"
end
end
def self.new_resources(resource, options)
ParseableResources.new(resource, options)
end
# NOTE: skipping `ParseableProperties`, we probably won't support that
end
hocon-1.3.1/lib/hocon/impl/config_string.rb 0000644 0000041 0000041 00000003635 13704151031 020651 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/abstract_config_value'
require 'hocon/config_value_type'
require 'hocon/impl/config_impl_util'
class Hocon::Impl::ConfigString
include Hocon::Impl::AbstractConfigValue
ConfigImplUtil = Hocon::Impl::ConfigImplUtil
attr_reader :value
class Quoted < Hocon::Impl::ConfigString
def initialize(origin, value)
super(origin, value)
end
def new_copy(origin)
self.class.new(origin, @value)
end
private
# serialization all goes through SerializedConfigValue
def write_replace
Hocon::Impl::SerializedConfigValue.new(self)
end
end
# 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()
class Unquoted < Hocon::Impl::ConfigString
def initialize(origin, value)
super(origin, value)
end
def new_copy(origin)
self.class.new(origin, @value)
end
def write_replace
Hocon::Impl::SerializedConfigValue.new(self)
end
end
def was_quoted?
self.is_a?(Quoted)
end
def value_type
Hocon::ConfigValueType::STRING
end
def unwrapped
@value
end
def transform_to_string
@value
end
def render_value_to_sb(sb, indent_size, at_root, options)
if options.json?
sb << ConfigImplUtil.render_json_string(@value)
else
sb << ConfigImplUtil.render_string_unquoted_if_possible(@value)
end
end
private
def initialize(origin, value)
super(origin)
@value = value
end
end
hocon-1.3.1/lib/hocon/impl/resolve_status.rb 0000644 0000041 0000041 00000000515 13704151031 021072 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
class Hocon::Impl::ResolveStatus
UNRESOLVED = 0
RESOLVED = 1
def self.from_values(values)
if values.any? { |v| v.resolve_status == UNRESOLVED }
UNRESOLVED
else
RESOLVED
end
end
def self.from_boolean(resolved)
resolved ? RESOLVED : UNRESOLVED
end
end
hocon-1.3.1/lib/hocon/impl/container.rb 0000644 0000041 0000041 00000002020 13704151031 017763 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/config_value'
require 'hocon/config_error'
# 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.
module Hocon::Impl::Container
include Hocon::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.
#
def replace_child(child, replacement)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Container` must implement `replace_child` (#{self.class})"
end
#
# Super-expensive full traversal to see if descendant is anywhere
# underneath this container.
#
def has_descendant?(descendant)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Container` must implement `has_descendant?` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/impl/config_document_parser.rb 0000644 0000041 0000041 00000054104 13704151031 022532 0 ustar www-data www-data # encoding: utf-8
require 'stringio'
require 'hocon/impl'
require 'hocon/config_error'
require 'hocon/impl/tokens'
require 'hocon/impl/config_node_single_token'
require 'hocon/impl/config_node_comment'
require 'hocon/impl/abstract_config_node_value'
require 'hocon/impl/config_node_concatenation'
require 'hocon/impl/config_include_kind'
require 'hocon/impl/config_node_object'
require 'hocon/impl/config_node_array'
require 'hocon/impl/config_node_root'
class Hocon::Impl::ConfigDocumentParser
ConfigSyntax = Hocon::ConfigSyntax
ConfigParseError = Hocon::ConfigError::ConfigParseError
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
ConfigValueType = Hocon::ConfigValueType
Tokens = Hocon::Impl::Tokens
PathParser = Hocon::Impl::PathParser
ArrayIterator = Hocon::Impl::ArrayIterator
ConfigImplUtil = Hocon::Impl::ConfigImplUtil
ConfigIncludeKind = Hocon::Impl::ConfigIncludeKind
ConfigNodeSingleToken = Hocon::Impl::ConfigNodeSingleToken
ConfigNodeSimpleValue = Hocon::Impl::ConfigNodeSimpleValue
ConfigNodeInclude = Hocon::Impl::ConfigNodeInclude
ConfigNodeField = Hocon::Impl::ConfigNodeField
ConfigNodeObject = Hocon::Impl::ConfigNodeObject
ConfigNodeArray = Hocon::Impl::ConfigNodeArray
ConfigNodeRoot = Hocon::Impl::ConfigNodeRoot
def self.parse(tokens, origin, options)
syntax = options.syntax.nil? ? ConfigSyntax::CONF : options.syntax
context = Hocon::Impl::ConfigDocumentParser::ParseContext.new(syntax, origin, tokens)
context.parse
end
def self.parse_value(tokens, origin, options)
syntax = options.syntax.nil? ? ConfigSyntax::CONF : options.syntax
context = Hocon::Impl::ConfigDocumentParser::ParseContext.new(syntax, origin, tokens)
context.parse_single_value
end
class ParseContext
def initialize(flavor, origin, tokens)
@line_number = 1
@buffer = []
@tokens = tokens
@flavor = flavor
@equals_count = 0
@base_origin = origin
end
def pop_token
if @buffer.empty?
return @tokens.next
end
@buffer.pop
end
def next_token
t = pop_token
if @flavor.equal?(ConfigSyntax::JSON)
if Tokens.unquoted_text?(t) && !unquoted_whitespace?(t)
raise parse_error("Token not allowed in valid JSON: '#{Tokens.unquoted_text(t)}'")
elsif Tokens.substitution?(t)
raise parse_error("Substitutions (${} syntax) not allowed in JSON")
end
end
t
end
def next_token_collecting_whitespace(nodes)
while true
t = next_token
if Tokens.ignored_whitespace?(t) || Tokens.newline?(t) || unquoted_whitespace?(t)
nodes.push(ConfigNodeSingleToken.new(t))
if Tokens.newline?(t)
@line_number = t.line_number + 1
end
elsif Tokens.comment?(t)
nodes.push(Hocon::Impl::ConfigNodeComment.new(t))
else
new_number = t.line_number
if new_number >= 0
@line_number = new_number
end
return t
end
end
end
def put_back(token)
@buffer.push(token)
end
# 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.
def check_element_separator(nodes)
if @flavor.equal?(ConfigSyntax::JSON)
t = next_token_collecting_whitespace(nodes)
if t.equal?(Tokens::COMMA)
nodes.push(ConfigNodeSingleToken.new(t))
return true
else
put_back(t)
return false
end
else
saw_separator_or_new_line = false
t = next_token
while true
if Tokens.ignored_whitespace?(t) || unquoted_whitespace?(t)
nodes.push(ConfigNodeSingleToken.new(t))
elsif Tokens.comment?(t)
nodes.push(Hocon::Impl::ConfigNodeComment.new(t))
elsif Tokens.newline?(t)
saw_separator_or_new_line = true
@line_number += 1
nodes.push(ConfigNodeSingleToken.new(t))
# we want to continue to also eat
# a comma if there is one.
elsif t.equal?(Tokens::COMMA)
nodes.push(ConfigNodeSingleToken.new(t))
return true
else
# non-newline-or-comma
put_back(t)
return saw_separator_or_new_line
end
t = next_token
end
end
end
# parse a concatenation. If there is no concatenation, return the next value
def consolidate_values(nodes)
# this trick is not done in JSON
if @flavor.equal?(ConfigSyntax::JSON)
return nil
end
# create only if we have value tokens
values = []
value_count = 0
# ignore a newline up front
t = next_token_collecting_whitespace(nodes)
while true
v = nil
if Tokens.ignored_whitespace?(t)
values.push(ConfigNodeSingleToken.new(t))
t = next_token
next
elsif Tokens.value?(t) || Tokens.unquoted_text?(t) || Tokens.substitution?(t) || t == Tokens::OPEN_CURLY || t == Tokens::OPEN_SQUARE
# there may be newlines _within_ the objects and arrays
v = parse_value(t)
value_count += 1
else
break
end
if v.nil?
raise ConfigBugOrBrokenError, "no value"
end
values.push(v)
t = next_token # but don't consolidate across a newline
end
put_back(t)
# No concatenation was seen, but a single value may have been parsed, so return it, and put back
# all succeeding tokens
if value_count < 2
value = nil
values.each do |node|
if node.is_a?(Hocon::Impl::AbstractConfigNodeValue)
value = node
elsif value.nil?
nodes.add(node)
else
put_back(node.tokens[0])
end
end
return value
end
# Put back any trailing whitespace, as the parent object is responsible for tracking
# any leading/trailing whitespace
for i in (0..values.size - 1).reverse_each
if values[i].is_a?(ConfigNodeSingleToken)
put_back(values[i].token)
values.delete_at(i)
else
break
end
end
Hocon::Impl::ConfigNodeConcatenation.new(values)
end
def parse_error(message, cause = nil)
ConfigParseError.new(@base_origin.with_line_number(@line_number), message, cause)
end
def add_quote_suggestion(bad_token, message, last_path = nil, inside_equals = nil)
if inside_equals.nil?
inside_equals = @equals_count > 0
end
previous_field_name = last_path != nil ? last_path.render : nil
if bad_token == Tokens::EOF.to_s
# EOF requires special handling for the error to make sense.
if previous_field_name != nil
part = "#{message} (if you intended '#{previous_field_name}'" +
"' to be part of a value, instead of a key, " +
"try adding double quotes around the whole value"
else
return message
end
else
if previous_field_name != nil
part = "#{message} (if you intended #{bad_token}" +
" to be part of the value for '#{previous_field_name}', " +
"try enclosing the value in double quotes"
else
part = "#{message} (if you intended #{bad_token}" +
" to be part of a key or string value, " +
"try enclosing the key or value in double quotes"
end
end
# Don't have a special case to throw a message about changing the file to .properties, since
# we don't support that format
part
end
def parse_value(t)
v = nil
starting_equals_count = @equals_count
if Tokens.value?(t) || Tokens.unquoted_text?(t) || Tokens.substitution?(t)
v = Hocon::Impl::ConfigNodeSimpleValue.new(t)
elsif t.equal?(Tokens::OPEN_CURLY)
v = parse_object(true)
elsif t.equal?(Tokens::OPEN_SQUARE)
v = parse_array
else
raise parse_error(add_quote_suggestion(t.to_s, "Expecting a value but got wrong token: #{t}"))
end
if @equals_count != starting_equals_count
raise ConfigBugOrBrokenError, "Bug in config parser: unbalanced equals count"
end
v
end
def parse_key(token)
if @flavor.equal?(ConfigSyntax::JSON)
if Tokens.value_with_type?(token, ConfigValueType::STRING)
return PathParser.parse_path_node_expression(Hocon::Impl::ArrayIterator.new([token]), nil)
else
raise ConfigParseError, "Expecting close brace } or a field name here, got #{token}"
end
else
expression = []
t = token
while Tokens.value?(t) || Tokens.unquoted_text?(t)
expression.push(t)
t = next_token # note: don't cross a newline
end
if expression.empty?
raise parse_error("expecting a close brace or a field name here, got #{t}")
end
put_back(t) # put back the token we ended with
PathParser.parse_path_node_expression(ArrayIterator.new(expression), nil)
end
end
def include_keyword?(t)
Tokens.unquoted_text?(t) && Tokens.unquoted_text(t) == "include"
end
def unquoted_whitespace?(t)
unless Tokens.unquoted_text?(t)
return false
end
s = Tokens.unquoted_text(t)
s.each_char do |c|
unless ConfigImplUtil.whitespace?(c)
return false
end
end
true
end
def key_value_separator?(t)
if @flavor.equal?(ConfigSyntax::JSON)
t.equal?(Tokens::COLON)
else
t.equal?(Tokens::COLON) || t.equal?(Tokens::EQUALS) || t.equal?(Tokens::PLUS_EQUALS)
end
end
def parse_include(children)
t = next_token_collecting_whitespace(children)
# we either have a quoted string or the "file()" syntax
if Tokens.unquoted_text?(t)
# get foo(
kind_text = Tokens.unquoted_text(t)
if kind_text == "url("
kind = ConfigIncludeKind::URL
elsif kind_text == "file("
kind = ConfigIncludeKind::FILE
elsif kind_text == "classpath("
kind = ConfigIncludeKind::CLASSPATH
else
raise parse_error("expecting include parameter to be quoted filename, file(), classpath(), or url(). No spaces are allowed before the open paren. Not expecting: #{t}")
end
children.push(ConfigNodeSingleToken.new(t))
# skip space inside parens
t = next_token_collecting_whitespace(children)
# quoted string
unless Tokens.value_with_type?(t, ConfigValueType::STRING)
raise parse_error("expecting a quoted string inside file(), classpath(), or url(), rather than: #{t}")
end
children.push(ConfigNodeSimpleValue.new(t))
# skip space after string, inside parens
t = next_token_collecting_whitespace(children)
if Tokens.unquoted_text?(t) && Tokens.unquoted_text(t) == ")"
# OK, close paren
else
raise parse_error("expecting a close parentheses ')' here, not: #{t}")
end
ConfigNodeInclude.new(children, kind)
elsif Tokens.value_with_type?(t, ConfigValueType::STRING)
children.push(ConfigNodeSimpleValue.new(t))
ConfigNodeInclude.new(children, ConfigIncludeKind::HEURISTIC)
else
raise parse_error("include keyword is not followed by a quoted string, but by: #{t}")
end
end
def parse_object(had_open_curly)
# invoked just after the OPEN_CURLY (or START, if !hadOpenCurly)
after_comma = false
last_path = nil
last_inside_equals = false
object_nodes = []
keys = Hash.new
if had_open_curly
object_nodes.push(ConfigNodeSingleToken.new(Tokens::OPEN_CURLY))
end
while true
t = next_token_collecting_whitespace(object_nodes)
if t.equal?(Tokens::CLOSE_CURLY)
if @flavor.equal?(ConfigSyntax::JSON) && after_comma
raise parse_error(add_quote_suggestion(t.to_s,
"expecting a field name after a comma, got a close brace } instead"))
elsif !had_open_curly
raise parse_error(add_quote_suggestion(t.to_s,
"unbalanced close brace '}' with no open brace"))
end
object_nodes.push(ConfigNodeSingleToken.new(Tokens::CLOSE_CURLY))
break
elsif t.equal?(Tokens::EOF) && !had_open_curly
put_back(t)
break
elsif !@flavor.equal?(ConfigSyntax::JSON) && include_keyword?(t)
include_nodes = []
include_nodes.push(ConfigNodeSingleToken.new(t))
object_nodes.push(parse_include(include_nodes))
after_comma = false
else
key_value_nodes = []
key_token = t
path = parse_key(key_token)
key_value_nodes.push(path)
after_key = next_token_collecting_whitespace(key_value_nodes)
inside_equals = false
if @flavor.equal?(ConfigSyntax::CONF) && after_key.equal?(Tokens::OPEN_CURLY)
# can omit the ':' or '=' before an object value
next_value = parse_value(after_key)
else
unless key_value_separator?(after_key)
raise parse_error(add_quote_suggestion(after_key.to_s,
"Key '#{path.render()}' may not be followed by token: #{after_key}"))
end
key_value_nodes.push(ConfigNodeSingleToken.new(after_key))
if after_key.equal?(Tokens::EQUALS)
inside_equals = true
@equals_count += 1
end
next_value = consolidate_values(key_value_nodes)
if next_value.nil?
next_value = parse_value(next_token_collecting_whitespace(key_value_nodes))
end
end
key_value_nodes.push(next_value)
if inside_equals
@equals_count -= 1
end
last_inside_equals = inside_equals
key = path.value.first
remaining = path.value.remainder
if remaining.nil?
existing = keys[key]
unless existing.nil?
# 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.equal?(ConfigSyntax::JSON)
raise parse_error("JSON does not allow duplicate fields: '#{key}' was already seen")
end
end
keys[key] = true
else
if @flavor.equal?(ConfigSyntax::JSON)
raise ConfigBugOrBrokenError, "somehow got multi-element path in JSON mode"
end
keys[key] = true
end
after_comma = false
object_nodes.push(ConfigNodeField.new(key_value_nodes))
end
if check_element_separator(object_nodes)
# continue looping
after_comma = true
else
t = next_token_collecting_whitespace(object_nodes)
if t.equal?(Tokens::CLOSE_CURLY)
unless had_open_curly
raise parse_error(add_quote_suggestion(t.to_s,
"unbalanced close brace '}' with no open brace",
last_path,
last_inside_equals,))
end
object_nodes.push(ConfigNodeSingleToken.new(t))
break
elsif had_open_curly
raise parse_error(add_quote_suggestion(t.to_s,
"Expecting close brace } or a comma, got #{t}",
last_path,
last_inside_equals,))
else
if t.equal?(Tokens::EOF)
put_back(t)
break
else
raise parse_error(add_quote_suggestion(t.to_s,
"Expecting close brace } or a comma, got #{t}",
last_path,
last_inside_equals,))
end
end
end
end
ConfigNodeObject.new(object_nodes)
end
def parse_array
children = []
children.push(ConfigNodeSingleToken.new(Tokens::OPEN_SQUARE))
# invoked just after the OPEN_SQUARE
t = nil
next_value = consolidate_values(children)
unless next_value.nil?
children.push(next_value)
else
t = next_token_collecting_whitespace(children)
# special-case the first element
if t.equal?(Tokens::CLOSE_SQUARE)
children.push(ConfigNodeSingleToken.new(t))
return ConfigNodeArray.new(children)
elsif Tokens.value?(t) || t.equal?(Tokens::OPEN_CURLY) ||
t.equal?(Tokens::OPEN_SQUARE) || Tokens.unquoted_text?(t) ||
Tokens.substitution?(t)
next_value = parse_value(t)
children.push(next_value)
else
raise parse_error("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)")
end
end
# now remaining elements
while true
# just after a value
if check_element_separator(children)
# comma (or newline equivalent) consumed
else
t = next_token_collecting_whitespace(children)
if t.equal?(Tokens::CLOSE_SQUARE)
children.push(ConfigNodeSingleToken.new(t))
return ConfigNodeArray.new(children)
else
raise parse_error("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)")
end
end
# now just after a comma
next_value = consolidate_values(children)
unless next_value.nil?
children.push(next_value)
else
t = next_token_collecting_whitespace(children)
if Tokens.value?(t) || t.equal?(Tokens::OPEN_CURLY) ||
t.equal?(Tokens::OPEN_SQUARE) || Tokens.unquoted_text?(t) ||
Tokens.substitution?(t)
next_value = parse_value(t)
children.push(next_value)
elsif !@flavor.equal?(ConfigSyntax::JSON) && t.equal?(Tokens::CLOSE_SQUARE)
# we allow one trailing comma
put_back(t)
else
raise parse_error("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)")
end
end
end
end
def parse
children = []
t = next_token
if t.equal?(Tokens::START)
# OK
else
raise ConfigBugOrBrokenException, "token stream did not begin with START, had #{t}"
end
t = next_token_collecting_whitespace(children)
result = nil
missing_curly = false
if t.equal?(Tokens::OPEN_CURLY) || t.equal?(Tokens::OPEN_SQUARE)
result = parse_value(t)
else
if @flavor.equal?(ConfigSyntax::JSON)
if t.equal?(Tokens::EOF)
raise parse_error("Empty document")
else
raise parse_error("Document must have an object or array at root, unexpected token: #{t}")
end
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.
put_back(t)
missing_curly = true
result = parse_object(false)
end
end
# 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.is_a?(ConfigNodeObject) && missing_curly
children += result.children
else
children.push(result)
end
t = next_token_collecting_whitespace(children)
if t.equal?(Tokens::EOF)
if missing_curly
# If there were no braces, the entire document should be treated as a single object
ConfigNodeRoot.new([ConfigNodeObject.new(children)], @base_origin)
else
ConfigNodeRoot.new(children, @base_origin)
end
else
raise parse_error("Document has trailing tokens after first object or array: #{t}")
end
end
# Parse a given input stream into a single value node. Used when doing a replace inside a ConfigDocument.
def parse_single_value
t = next_token
if t.equal?(Tokens::START)
# OK
else
raise ConfigBugOrBrokenError, "token stream did not begin with START, had #{t}"
end
t = next_token
if Tokens.ignored_whitespace?(t) || Tokens.newline?(t) || unquoted_whitespace?(t) || Tokens.comment?(t)
raise parse_error("The value from setValue cannot have leading or trailing newlines, whitespace, or comments")
end
if t.equal?(Tokens::EOF)
raise parse_error("Empty value")
end
if @flavor.equal?(ConfigSyntax::JSON)
node = parse_value(t)
t = next_token
if t.equal?(Tokens::EOF)
return node
else
raise parse_error("Parsing JSON and the value set in setValue was either a concatenation or had trailing whitespace, newlines, or comments")
end
else
put_back(t)
nodes = []
node = consolidate_values(nodes)
t = next_token
if t.equal?(Tokens::EOF)
node
else
raise parse_error("The value from setValue cannot have leading or trailing newlines, whitespace, or comments")
end
end
end
end
end hocon-1.3.1/lib/hocon/impl/config_node_single_token.rb 0000644 0000041 0000041 00000000411 13704151031 023016 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/abstract_config_node'
class Hocon::Impl::ConfigNodeSingleToken
include Hocon::Impl::AbstractConfigNode
def initialize(t)
@token = t
end
attr_reader :token
def tokens
[@token]
end
end hocon-1.3.1/lib/hocon/impl/config_double.rb 0000644 0000041 0000041 00000001301 13704151031 020601 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/config_number'
class Hocon::Impl::ConfigDouble < Hocon::Impl::ConfigNumber
def initialize(origin, value, original_text)
super(origin, original_text)
@value = value
end
attr_reader :value
def value_type
Hocon::ConfigValueType::NUMBER
end
def unwrapped
@value
end
def transform_to_string
s = super
if s.nil?
@value.to_s
else
s
end
end
def long_value
@value.to_i
end
def double_value
@value
end
def new_copy(origin)
self.class.new(origin, @value, original_text)
end
# NOTE: skipping `writeReplace` from upstream, because it involves serialization
end
hocon-1.3.1/lib/hocon/impl/token.rb 0000644 0000041 0000041 00000001607 13704151031 017133 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/token_type'
class Hocon::Impl::Token
attr_reader :token_type, :token_text
def self.new_without_origin(token_type, debug_string, token_text)
Hocon::Impl::Token.new(token_type, nil, token_text, debug_string)
end
def initialize(token_type, origin, token_text = nil, debug_string = nil)
@token_type = token_type
@origin = origin
@token_text = token_text
@debug_string = debug_string
end
attr_reader :origin
def line_number
if @origin
@origin.line_number
else
-1
end
end
def to_s
if !@debug_string.nil?
@debug_string
else
Hocon::Impl::TokenType.token_type_name(@token_type)
end
end
def ==(other)
# @origin deliberately left out
other.is_a?(Hocon::Impl::Token) && @token_type == other.token_type
end
def hash
@token_type.hash
end
end
hocon-1.3.1/lib/hocon/impl/config_node_comment.rb 0000644 0000041 0000041 00000001006 13704151031 022000 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/config_error'
require 'hocon/impl/config_node_single_token'
require 'hocon/impl/tokens'
class Hocon::Impl::ConfigNodeComment < Hocon::Impl::ConfigNodeSingleToken
def initialize(comment)
super(comment)
unless Hocon::Impl::Tokens.comment?(@token)
raise Hocon::ConfigError::ConfigBugOrBrokenError, 'Tried to create a ConfigNodeComment from a non-comment token'
end
end
def comment_text
Hocon::Impl::Tokens.comment_text(@token)
end
end hocon-1.3.1/lib/hocon/impl/abstract_config_object.rb 0000644 0000041 0000041 00000014344 13704151031 022473 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/abstract_config_value'
require 'hocon/impl/simple_config'
require 'hocon/config_object'
require 'hocon/config_value_type'
require 'hocon/impl/resolve_status'
require 'hocon/impl/simple_config_origin'
require 'hocon/config_error'
require 'hocon/impl/config_impl'
require 'hocon/impl/unsupported_operation_error'
require 'hocon/impl/container'
module Hocon::Impl::AbstractConfigObject
include Hocon::ConfigObject
include Hocon::Impl::Container
include Hocon::Impl::AbstractConfigValue
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError
def initialize(origin)
super(origin)
@config = Hocon::Impl::SimpleConfig.new(self)
end
def to_config
@config
end
def to_fallback_value
self
end
def with_only_key(key)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_only_key`"
end
def without_key(key)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `without_key`"
end
def with_value(key, value)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_value`"
end
def with_only_path_or_nil(path)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_only_path_or_nil`"
end
def with_only_path(path)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_only_path`"
end
def without_path(path)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `without_path`"
end
def with_path_value(path, value)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `with_path_value`"
end
# 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
# ConfigNotResolvedError will be thrown.
#
# @param key
# @return the unmodified raw value or null
def peek_assuming_resolved(key, original_path)
begin
attempt_peek_with_partial_resolve(key)
rescue ConfigNotResolvedError => e
raise Hocon::Impl::ConfigImpl.improve_not_resolved(original_path, e)
end
end
# 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 ConfigNotResolvedError
# if can't figure out key's value (or existence) without more
# resolving
def attempt_peek_with_partial_resolve(key)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `attempt_peek_with_partial_resolve`"
end
# 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.
def peek_path(path)
peek_path_from_obj(self, path)
end
def peek_path_from_obj(obj, path)
begin
# we'll fail if anything along the path can't be looked at without resolving
path_next = path.remainder
v = obj.attempt_peek_with_partial_resolve(path.first)
if path_next.nil?
v
else
if v.is_a?(Hocon::Impl::AbstractConfigObject)
peek_path_from_obj(v, path_next)
else
nil
end
end
rescue ConfigNotResolvedError => e
raise Hocon::Impl::ConfigImpl.improve_not_resolved(path, e)
end
end
def value_type
Hocon::ConfigValueType::OBJECT
end
def new_copy_with_status(status, origin)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `new_copy_with_status`"
end
def new_copy(origin)
new_copy_with_status(resolve_status, origin)
end
def construct_delayed_merge(origin, stack)
Hocon::Impl::ConfigDelayedMergeObject.new(origin, stack)
end
def merged_with_object(fallback)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `merged_with_object`"
end
def with_fallback(mergeable)
super(mergeable)
end
def self.merge_origins(stack)
if stack.empty?
raise ConfigBugOrBrokenError, "can't merge origins on empty list"
end
origins = []
first_origin = nil
num_merged = 0
stack.each do |v|
if first_origin.nil?
first_origin = v.origin
end
if (v.is_a?(Hocon::Impl::AbstractConfigObject)) &&
(v.resolve_status == Hocon::Impl::ResolveStatus::RESOLVED) &&
v.empty?
# don't include empty files or the .empty()
# config in the description, since they are
# likely to be "implementation details"
else
origins.push(v.origin)
num_merged += 1
end
end
if num_merged == 0
# the configs were all empty, so just use the first one
origins.push(first_origin)
end
Hocon::Impl::SimpleConfigOrigin.merge_origins(origins)
end
def resolve_substitutions(context, source)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `resolve_substituions`"
end
def relativized(path)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `relativized`"
end
def [](key)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `[]`"
end
def render_value_to_sb(sb, indent, at_root, options)
raise ConfigBugOrBrokenError, "subclasses of AbstractConfigObject should override `render_value_to_sb`"
end
def we_are_immutable(method)
Hocon::Impl::UnsupportedOperationError.new("ConfigObject is immutable, you can't call Map.#{method}")
end
def clear
raise we_are_immutable("clear")
end
def []=(key, value)
raise we_are_immutable("[]=")
end
def putAll(map)
raise we_are_immutable("putAll")
end
def remove(key)
raise we_are_immutable("remove")
end
def delete(key)
raise we_are_immutable("delete")
end
def with_origin(origin)
super(origin)
end
end
hocon-1.3.1/lib/hocon/impl/config_delayed_merge_object.rb 0000644 0000041 0000041 00000020605 13704151031 023453 0 ustar www-data www-data require 'hocon/impl'
require 'hocon/impl/unmergeable'
require 'hocon/impl/replaceable_merge_stack'
# This is just like ConfigDelayedMerge except we know statically
# that it will turn out to be an object.
class Hocon::Impl::ConfigDelayedMergeObject
include Hocon::Impl::Unmergeable
include Hocon::Impl::ReplaceableMergeStack
include Hocon::Impl::AbstractConfigObject
def initialize(origin, stack)
super(origin)
@stack = stack
if stack.empty?
raise Hocon::ConfigError::ConfigBugOrBrokenError.new("creating empty delayed merge value", nil)
end
if !@stack[0].is_a? Hocon::Impl::AbstractConfigObject
error_message = "created a delayed merge object not guaranteed to be an object"
raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil)
end
stack.each do |v|
if v.is_a?(Hocon::Impl::ConfigDelayedMergeObject) || v.is_a?(Hocon::Impl::ConfigDelayedMergeObject)
error_message = "placed nested DelayedMerge in a ConfigDelayedMerge, should have consolidated stack"
raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil)
end
end
end
attr_reader :stack
def new_copy(status, origin)
if status != resolve_status
raise Hocon::ConfigError::ConfigBugOrBrokenError.new(
"attempt to create resolved ConfigDelayedMergeObject")
end
Hocon::Impl::ConfigDelayedMergeObject.new(origin, @stack)
end
def resolve_substitutions(context, source)
merged = Hocon::Impl::ConfigDelayedMerge.resolve_substitutions(self, @stack, context, source)
merged.as_object_result
end
def make_replacement(context, skipping)
Hocon::Impl::ConfigDelayedMerge.make_replacement(context, @stack, skipping)
end
def resolve_status
Hocon::Impl::ResolveStatus::UNRESOLVED
end
def replace_child(child, replacement)
new_stack = Hocon::Impl::AbstractConfigValue.replace_child_in_list(@stack, child, replacement)
if new_stack == nil
nil
else
self.class.new(origin, new_stack)
end
end
def has_descendant?(descendant)
Hocon::Impl::AbstractConfigValue.has_descendant_in_list?(@stack, descendant)
end
def relativized(prefix)
new_stack = []
@stack.each { |o|
new_stack << o.relativized(prefix)
}
self.class.new(origin, new_stack)
end
def ignores_fallbacks?
Hocon::Impl::ConfigDelayedMerge.stack_ignores_fallbacks?(@stack)
end
def merged_with_the_unmergeable(fallback)
require_not_ignoring_fallbacks
merged_stack_with_the_unmergeable(@stack, fallback)
end
def merged_with_object(fallback)
merged_with_non_object(fallback)
end
def merged_with_non_object(fallback)
require_not_ignoring_fallbacks
merged_stack_with_non_object(@stack, fallback)
end
# No implementation of withFallback here,
# just use the implementation in the super-class
def with_only_key(key)
raise self.class.not_resolved
end
def without_key(key)
raise self.class.not_resolved
end
def with_only_path_or_nil(key)
raise self.class.not_resolved
end
def with_only_path(key)
raise self.class.not_resolved
end
def without_path(key)
raise self.class.not_resolved
end
def with_value(key_or_path, value = nil)
raise self.class.not_resolved
end
def unmerged_values
@stack
end
def can_equal(other)
other.is_a? Hocon::Impl::ConfigDelayedMergeObject
end
def ==(other)
# note that "origin" is deliberately NOT part of equality
if other.is_a? Hocon::Impl::ConfigDelayedMergeObject
can_equal(other) && (@stack == other.stack || @stack.equal?(other.stack))
else
false
end
end
def hash
# note that "origin" is deliberately NOT part of equality
@stack.hash
end
def render_to_sb(sb, indent, at_root, at_key, options)
Hocon::Impl::ConfigDelayedMerge.render_value_to_sb_from_stack(@stack, sb, indent, at_root, at_key, options)
end
def self.not_resolved
error_message = "need to Config#resolve() before using this object, see the API docs for Config#resolve()"
Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil)
end
def unwrapped
raise self.class.not_resolved
end
def [](key)
raise self.class.not_resolved
end
def has_key?(key)
raise self.class.not_resolved
end
def has_value?(value)
raise self.class.not_resolved
end
def each
raise self.class.not_resolved
end
def empty?
raise self.class.not_resolved
end
def keys
raise self.class.not_resolved
end
def values
raise self.class.not_resolved
end
def size
raise self.class.not_resolved
end
def self.unmergeable?(object)
# Ruby note: This is the best way I could find to simulate
# else if (layer instanceof Unmergeable) in java since we're including
# the Unmergeable module instead of extending an Unmergeable class
object.class.included_modules.include?(Hocon::Impl::Unmergeable)
end
def attempt_peek_with_partial_resolve(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.
@stack.each do |layer|
if layer.is_a?(Hocon::Impl::AbstractConfigObject)
v = layer.attempt_peek_with_partial_resolve(key)
if !v.nil?
if v.ignores_fallbacks?
# 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.
next
end
elsif self.class.unmergeable?(layer)
error_message = "should not be reached: unmergeable object returned null value"
raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil)
else
# a non-unmergeable AbstractConfigObject that returned null
# for the key in question is not relevant, we can keep
# looking for a value.
next
end
elsif self.class.unmergeable?(layer)
error_message = "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"
raise Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil)
elsif layer.resolved_status == 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.is_a?(Hocon::Impl::ConfigList)
error_message = "Expecting a list here, not #{layer}"
raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil)
end
return nil
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.ignores_fallbacks?
error_message = "resolved non-object should ignore fallbacks"
raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil)
end
return nil
end
end
# If we get here, then we never found anything unresolved which means
# the ConfigDelayedMergeObject should not have existed. some
# invariant was violated.
error_message = "Delayed merge stack does not contain any unmergeable values"
raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil)
end
end
hocon-1.3.1/lib/hocon/impl/config_node_field.rb 0000644 0000041 0000041 00000003574 13704151031 021435 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/config_error'
require 'hocon/impl/abstract_config_node'
require 'hocon/impl/abstract_config_node_value'
require 'hocon/impl/config_node_comment'
require 'hocon/impl/config_node_path'
require 'hocon/impl/config_node_single_token'
require 'hocon/impl/tokens'
class Hocon::Impl::ConfigNodeField
include Hocon::Impl::AbstractConfigNode
Tokens = Hocon::Impl::Tokens
def initialize(children)
@children = children
end
attr_reader :children
def tokens
tokens = []
@children.each do |child|
tokens += child.tokens
end
tokens
end
def replace_value(new_value)
children_copy = @children.clone
children_copy.each_with_index do |child, i|
if child.is_a?(Hocon::Impl::AbstractConfigNodeValue)
children_copy[i] = new_value
return self.class.new(children_copy)
end
end
raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a value"
end
def value
@children.each do |child|
if child.is_a?(Hocon::Impl::AbstractConfigNodeValue)
return child
end
end
raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a value"
end
def path
@children.each do |child|
if child.is_a?(Hocon::Impl::ConfigNodePath)
return child
end
end
raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a path"
end
def separator
@children.each do |child|
if child.is_a?(Hocon::Impl::ConfigNodeSingleToken)
t = child.token
if t == Tokens::PLUS_EQUALS or t == Tokens::COLON or t == Tokens::EQUALS
return t
end
end
end
nil
end
def comments
comments = []
@children.each do |child|
if child.is_a?(Hocon::Impl::ConfigNodeComment)
comments << child.comment_text
end
end
comments
end
end hocon-1.3.1/lib/hocon/impl/resolve_context.rb 0000644 0000041 0000041 00000020210 13704151031 021225 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_error'
require 'hocon/impl/resolve_source'
require 'hocon/impl/resolve_memos'
require 'hocon/impl/memo_key'
require 'hocon/impl/abstract_config_value'
require 'hocon/impl/config_impl'
class Hocon::Impl::ResolveContext
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
NotPossibleToResolve = Hocon::Impl::AbstractConfigValue::NotPossibleToResolve
attr_reader :restrict_to_child
def initialize(memos, options, restrict_to_child, resolve_stack, cycle_markers)
@memos = memos
@options = options
@restrict_to_child = restrict_to_child
@resolve_stack = resolve_stack
@cycle_markers = cycle_markers
end
def self.new_cycle_markers
# This looks crazy, but wtf else should we do with
# return Collections.newSetFromMap(new IdentityHashMap());
Set.new
end
def add_cycle_marker(value)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("++ Cycle marker #{value}@#{value.hash}",
depth)
end
if @cycle_markers.include?(value)
raise ConfigBugOrBrokenError.new("Added cycle marker twice " + value)
end
copy = self.class.new_cycle_markers
copy.merge(@cycle_markers)
copy.add(value)
self.class.new(@memos, @options, @restrict_to_child, @resolve_stack, copy)
end
def remove_cycle_marker(value)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("-- Cycle marker #{value}@#{value.hash}",
depth)
end
copy = self.class.new_cycle_markers
copy.merge(@cycle_markers)
copy.delete(value)
self.class.new(@memos, @options, @restrict_to_child, @resolve_stack, copy)
end
def memoize(key, value)
changed = @memos.put(key, value)
self.class.new(changed, @options, @restrict_to_child, @resolve_stack, @cycle_markers)
end
def options
@options
end
def is_restricted_to_child
@restrict_to_child != nil
end
def restrict(restrict_to)
if restrict_to.equal?(@restrict_to_child)
self
else
Hocon::Impl::ResolveContext.new(@memos, @options, restrict_to, @resolve_stack, @cycle_markers)
end
end
def unrestricted
restrict(nil)
end
def resolve(original, source)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"resolving #{original} restrict_to_child=#{@restrict_to_child} in #{source}",
depth)
end
push_trace(original).real_resolve(original, source).pop_trace
end
def real_resolve(original, source)
# a fully-resolved (no restrict_to_child) object can satisfy a
# request for a restricted object, so always check that first.
full_key = Hocon::Impl::MemoKey.new(original, nil)
restricted_key = nil
cached = @memos.get(full_key)
# 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 == nil && is_restricted_to_child
restricted_key = Hocon::Impl::MemoKey.new(original, @restrict_to_child)
cached = @memos.get(restricted_key)
end
if cached != nil
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"using cached resolution #{cached} for #{original} restrict_to_child #{@restrict_to_child}",
depth)
end
Hocon::Impl::ResolveResult.make(self, cached)
else
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"not found in cache, resolving #{original}@#{original.hash}",
depth)
end
if @cycle_markers.include?(original)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"Cycle detected, can't resolve; #{original}@#{original.hash}",
depth)
end
raise NotPossibleToResolve.new(self)
end
result = original.resolve_substitutions(self, source)
resolved = result.value
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"resolved to #{resolved}@#{resolved.hash} from #{original}@#{resolved.hash}",
depth)
end
with_memo = result.context
if resolved == nil || resolved.resolve_status == Hocon::Impl::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 Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"caching #{full_key} result #{resolved}",
depth)
end
with_memo = with_memo.memoize(full_key, 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 is_restricted_to_child
if restricted_key == nil
raise ConfigBugOrBrokenError.new("restricted_key should not be null here")
end
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"caching #{restricted_key} result #{resolved}",
depth)
end
with_memo = with_memo.memoize(restricted_key, resolved)
elsif @options.allow_unresolved
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"caching #{full_key} result #{resolved}",
depth)
end
with_memo = with_memo.memoize(full_key, resolved)
else
raise ConfigBugOrBrokenError.new(
"resolve_substitutions did not give us a resolved object")
end
end
Hocon::Impl::ResolveResult.make(with_memo, resolved)
end
end
# This method is a translation of the constructor in the Java version with signature
# ResolveContext(ConfigResolveOptions options, Path restrictToChild)
def self.construct(options, restrict_to_child)
context = self.new(Hocon::Impl::ResolveMemos.new,
options,
restrict_to_child,
[],
new_cycle_markers)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace(
"ResolveContext restrict to child #{restrict_to_child}", context.depth)
end
context
end
def trace_string
separator = ", "
sb = ""
@resolve_stack.each { |value|
if value.instance_of?(Hocon::Impl::ConfigReference)
sb << value.expression.to_s
sb << separator
end
}
if sb.length > 0
sb.chomp!(separator)
end
sb
end
def depth
if @resolve_stack.size > 30
raise Hocon::ConfigError::ConfigBugOrBrokenError.new("resolve getting too deep")
end
@resolve_stack.size
end
def self.resolve(value, root, options)
source = Hocon::Impl::ResolveSource.new(root)
context = construct(options, nil)
begin
context.resolve(value, source).value
rescue NotPossibleToResolve => e
# ConfigReference was supposed to catch NotPossibleToResolve
raise ConfigBugOrBrokenError(
"NotPossibleToResolve was thrown from an outermost resolve", e)
end
end
def pop_trace
copy = @resolve_stack.clone
old = copy.delete_at(@resolve_stack.size - 1)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("popped trace #{old}", depth - 1)
end
Hocon::Impl::ResolveContext.new(@memos, @options, @restrict_to_child, copy, @cycle_markers)
end
private
def push_trace(value)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("pushing trace #{value}", depth)
end
copy = @resolve_stack.clone
copy << value
Hocon::Impl::ResolveContext.new(@memos, @options, @restrict_to_child, copy, @cycle_markers)
end
end
hocon-1.3.1/lib/hocon/impl/config_include_kind.rb 0000644 0000041 0000041 00000000210 13704151031 021755 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
module Hocon::Impl::ConfigIncludeKind
URL = 0
FILE = 1
CLASSPATH = 2
HEURISTIC = 3
end
hocon-1.3.1/lib/hocon/impl/config_node_simple_value.rb 0000644 0000041 0000041 00000002430 13704151031 023025 0 ustar www-data www-data # encoding: utf-8
require 'hocon/config_error'
require 'hocon/impl'
require 'hocon/impl/abstract_config_node_value'
require 'hocon/impl/array_iterator'
require 'hocon/impl/config_reference'
require 'hocon/impl/config_string'
require 'hocon/impl/path_parser'
require 'hocon/impl/substitution_expression'
require 'hocon/impl/tokens'
class Hocon::Impl::ConfigNodeSimpleValue
include Hocon::Impl::AbstractConfigNodeValue
Tokens = Hocon::Impl::Tokens
def initialize(value)
@token = value
end
attr_reader :token
def tokens
[@token]
end
def value
if Tokens.value?(@token)
return Tokens.value(@token)
elsif Tokens.unquoted_text?(@token)
return Hocon::Impl::ConfigString::Unquoted.new(@token.origin, Tokens.unquoted_text(@token))
elsif Tokens.substitution?(@token)
expression = Tokens.get_substitution_path_expression(@token)
path = Hocon::Impl::PathParser.parse_path_expression(Hocon::Impl::ArrayIterator.new(expression), @token.origin)
optional = Tokens.get_substitution_optional(@token)
return Hocon::Impl::ConfigReference.new(@token.origin, Hocon::Impl::SubstitutionExpression.new(path, optional))
end
raise Hocon::ConfigError::ConfigBugOrBrokenError, 'ConfigNodeSimpleValue did not contain a valid value token'
end
end hocon-1.3.1/lib/hocon/impl/config_null.rb 0000644 0000041 0000041 00000000727 13704151031 020314 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/config_value_type'
class Hocon::Impl::ConfigNull
include Hocon::Impl::AbstractConfigValue
def initialize(origin)
super(origin)
end
def value_type
Hocon::ConfigValueType::NULL
end
def unwrapped
nil
end
def transform_to_string
"null"
end
def render_value_to_sb(sb, indent, at_root, options)
sb << "null"
end
def new_copy(origin)
self.class.new(origin)
end
end
hocon-1.3.1/lib/hocon/impl/config_node_concatenation.rb 0000644 0000041 0000041 00000000352 13704151031 023166 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/config_node_complex_value'
class Hocon::Impl::ConfigNodeConcatenation
include Hocon::Impl::ConfigNodeComplexValue
def new_node(nodes)
self.class.new(nodes)
end
end hocon-1.3.1/lib/hocon/impl/simple_config.rb 0000644 0000041 0000041 00000022135 13704151031 020630 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/config_value_type'
require 'hocon/config_resolve_options'
require 'hocon/impl/path'
require 'hocon/impl/default_transformer'
require 'hocon/impl/config_impl'
require 'hocon/impl/resolve_context'
require 'hocon/config_mergeable'
class Hocon::Impl::SimpleConfig
include Hocon::ConfigMergeable
ConfigMissingError = Hocon::ConfigError::ConfigMissingError
ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError
ConfigNullError = Hocon::ConfigError::ConfigNullError
ConfigWrongTypeError = Hocon::ConfigError::ConfigWrongTypeError
ConfigValueType = Hocon::ConfigValueType
Path = Hocon::Impl::Path
DefaultTransformer = Hocon::Impl::DefaultTransformer
attr_reader :object
def initialize(object)
@object = object
end
attr_reader :object
def root
@object
end
def origin
@object.origin
end
def resolve(options = Hocon::ConfigResolveOptions.defaults)
resolve_with(self, options)
end
def resolve_with(source, options)
resolved = Hocon::Impl::ResolveContext.resolve(@object, source.object, options)
if resolved.eql?(@object)
self
else
Hocon::Impl::SimpleConfig.new(resolved)
end
end
def self.find_key(me, key, expected, original_path)
v = me.peek_assuming_resolved(key, original_path)
if v.nil?
raise ConfigMissingError.new(nil, "No configuration setting found for key '#{original_path.render}'", nil)
end
if not expected.nil?
v = DefaultTransformer.transform(v, expected)
end
if v.value_type == ConfigValueType::NULL
raise ConfigNullError.new(v.origin,
(ConfigNullError.make_message(original_path.render,
(not expected.nil?) ? ConfigValueType.value_type_name(expected) : nil)),
nil)
elsif (not expected.nil?) && v.value_type != expected
raise ConfigWrongTypeError.new(v.origin,
"#{original_path.render} has type #{ConfigValueType.value_type_name(v.value_type)} " +
"rather than #{ConfigValueType.value_type_name(expected)}",
nil)
else
return v
end
end
def find(me, path, expected, original_path)
key = path.first
rest = path.remainder
if rest.nil?
self.class.find_key(me, key, expected, original_path)
else
o = self.class.find_key(me, key, ConfigValueType::OBJECT,
original_path.sub_path(0, original_path.length - rest.length))
raise "Error: object o is nil" unless not o.nil?
find(o, rest, expected, original_path)
end
end
def find3(path_expression, expected, original_path)
find(@object, path_expression, expected, original_path)
end
def find2(path_expression, expected)
path = Path.new_path(path_expression)
find3(path, expected, path)
end
def ==(other)
if other.is_a? Hocon::Impl::SimpleConfig
@object == other.object
else
false
end
end
def hash
41 * @object.hash
end
def self.find_key_or_null(me, key, expected, original_path)
v = me.peek_assuming_resolved(key, original_path)
if v.nil?
raise Hocon::ConfigError::ConfigMissingError.new(nil, original_path.render, nil)
end
if not expected.nil?
v = Hocon::Impl::DefaultTransformer.transform(v, expected)
end
if (not expected.nil?) && (v.value_type != expected && v.value_type != ConfigValueType::NULL)
raise Hocon::ConfigError::ConfigWrongTypeError.with_expected_actual(v.origin,
original_path.render,
ConfigValueType.value_type_name(expected),
ConfigValueType.value_type_name(v.value_type))
else
return v
end
end
def self.find_or_null(me, path, expected, original_path)
begin
key = path.first
remainder = path.remainder
if remainder.nil?
return self.find_key_or_null(me, key, expected, original_path)
else
o = find_key(me,
key,
ConfigValueType::OBJECT,
original_path.sub_path(0, original_path.length - remainder.length))
if o.nil?
raise "Missing key: #{key} on path: #{path}"
end
find_or_null(o, remainder, expected, original_path)
end
rescue Hocon::ConfigError::ConfigNotResolvedError
raise Hocon::Impl::ConfigImpl::improved_not_resolved(path, e)
end
end
def is_null?(path_expression)
path = Path.new_path(path_expression)
v = self.class.find_or_null(@object, path, nil, path)
v.value_type == ConfigValueType::NULL
end
def get_value(path)
parsed_path = Path.new_path(path)
find(@object, parsed_path, nil, parsed_path)
end
def get_boolean(path)
v = find2(path, ConfigValueType::BOOLEAN)
v.unwrapped
end
def get_config_number(path_expression)
path = Path.new_path(path_expression)
v = find(@object, path, ConfigValueType::NUMBER, path)
v.unwrapped
end
def get_int(path)
get_config_number(path)
end
def get_string(path)
v = find2(path, ConfigValueType::STRING)
v.unwrapped
end
def get_list(path)
find2(path, ConfigValueType::LIST)
end
def get_object(path)
find2(path, ConfigValueType::OBJECT)
end
def get_config(path)
get_object(path).to_config
end
def get_any_ref(path)
v = find2(path, nil)
v.unwrapped
end
def get_bytes(path)
size = null
begin
size = get_long(path)
rescue ConfigWrongTypeError => e
v = find2(path, ConfigValueType::STRING)
size = self.class.parse_bytes(v.unwrapped, v.origin, path)
end
size
end
def get_homogeneous_unwrapped_list(path, expected)
l = []
list = get_list(path)
list.each do |cv|
if !expected.nil?
v = DefaultTransformer.transform(cv, expected)
end
if v.value_type != expected
raise ConfigWrongTypeError.with_expected_actual(origin, path,
"list of #{ConfigValueType.value_type_name(expected)}",
"list of #{ConfigValueType.value_type_name(v.value_type)}")
end
l << v.unwrapped
end
l
end
def get_boolean_list(path)
get_homogeneous_unwrapped_list(path, ConfigValueType::BOOLEAN)
end
def get_number_list(path)
get_homogeneous_unwrapped_list(path, ConfigValueType::NUMBER)
end
def get_int_list(path)
l = []
numbers = get_homogeneous_wrapped_list(path, ConfigValueType::NUMBER)
numbers.each do |v|
l << v.int_value_range_checked(path)
end
l
end
def get_double_list(path)
l = []
numbers = get_number_list(path)
numbers.each do |n|
l << n.double_value
end
l
end
def get_string_list(path)
get_homogeneous_unwrapped_list(path, ConfigValueType::STRING)
end
def get_object_list(path)
get_homogeneous_wrapped_list(path, ConfigValueType::OBJECT)
end
def get_homogeneous_wrapped_list(path, expected)
l = []
list = get_list(path)
list.each do |cv|
if !expected.nil?
v = DefaultTransformer.transform(cv, expected)
end
if v.value_type != expected
raise ConfigWrongTypeError.with_expected_actual(origin, path,
"list of #{ConfigValueType.value_type_name(expected)}",
"list of #{ConfigValueType.value_type_name(v.value_type)}")
end
l << v
end
l
end
def has_path_peek(path_expression)
path = Path.new_path(path_expression)
begin
peeked = @object.peek_path(path)
rescue Hocon::ConfigError::ConfigNotResolvedError
raise Hocon::Impl::ConfigImpl.improved_not_resolved(path, e)
end
peeked
end
def has_path?(path_expression)
peeked = has_path_peek(path_expression)
(not peeked.nil?) && peeked.value_type != ConfigValueType::NULL
end
def has_path_or_null?(path)
peeked = has_path_peek(path)
not peeked.nil?
end
def empty?
@object.empty?
end
def at_key(key)
root.at_key(key)
end
# In java this is an overloaded version of atKey
def at_key_with_origin(origin, key)
root.at_key_with_origin(origin, key)
end
def with_only_path(path_expression)
path = Path.new_path(path_expression)
self.class.new(root.with_only_path(path))
end
def without_path(path_expression)
path = Path.new_path(path_expression)
self.class.new(root.without_path(path))
end
def with_value(path_expression, v)
path = Path.new_path(path_expression)
self.class.new(root.with_value(path, v))
end
def to_fallback_value
@object
end
def with_fallback(other)
@object.with_fallback(other).to_config
end
end
hocon-1.3.1/lib/hocon/impl/from_map_mode.rb 0000644 0000041 0000041 00000000735 13704151031 020620 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/config_error'
module Hocon::Impl::FromMapMode
KEYS_ARE_PATHS = 0
KEYS_ARE_KEYS = 1
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
def self.map_mode_name(from_map_mode)
case from_map_mode
when KEYS_ARE_PATHS then "KEYS_ARE_PATHS"
when KEYS_ARE_KEYS then "KEYS_ARE_KEYS"
else raise ConfigBugOrBrokenError.new("Unrecognized FromMapMode #{from_map_mode}")
end
end
end hocon-1.3.1/lib/hocon/impl/full_includer.rb 0000644 0000041 0000041 00000000114 13704151031 020632 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
class Hocon::Impl::FullIncluder
end hocon-1.3.1/lib/hocon/impl/config_int.rb 0000644 0000041 0000041 00000001222 13704151031 020123 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/config_number'
require 'hocon/config_value_type'
class Hocon::Impl::ConfigInt < Hocon::Impl::ConfigNumber
def initialize(origin, value, original_text)
super(origin, original_text)
@value = value
end
attr_reader :value
def value_type
Hocon::ConfigValueType::NUMBER
end
def unwrapped
@value
end
def transform_to_string
s = super
if s.nil?
self.to_s
else
s
end
end
def long_value
@value
end
def double_value
@value
end
def new_copy(origin)
Hocon::Impl::ConfigInt.new(origin, @value, @original_text)
end
end
hocon-1.3.1/lib/hocon/impl/config_node_complex_value.rb 0000644 0000041 0000041 00000003153 13704151031 023206 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/abstract_config_node_value'
require 'hocon/impl/config_node_field'
require 'hocon/impl/config_node_include'
require 'hocon/impl/config_node_single_token'
require 'hocon/impl/tokens'
require 'hocon/config_error'
module Hocon::Impl::ConfigNodeComplexValue
include Hocon::Impl::AbstractConfigNodeValue
def initialize(children)
@children = children
end
attr_reader :children
def tokens
tokens = []
@children.each do |child|
tokens += child.tokens
end
tokens
end
def indent_text(indentation)
children_copy = @children.clone
i = 0
while i < children_copy.size
child = children_copy[i]
if child.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Hocon::Impl::Tokens.newline?(child.token)
children_copy.insert(i + 1, indentation)
i += 1
elsif child.is_a?(Hocon::Impl::ConfigNodeField)
value = child.value
if value.is_a?(Hocon::Impl::ConfigNodeComplexValue)
children_copy[i] = child.replace_value(value.indent_text(indentation))
end
elsif child.is_a?(Hocon::Impl::ConfigNodeComplexValue)
children_copy[i] = child.indent_text(indentation)
end
i += 1
end
new_node(children_copy)
end
# 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
def new_node(nodes)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigNodeComplexValue should override `new_node` (#{self.class})"
end
end hocon-1.3.1/lib/hocon/impl/config_node_object.rb 0000644 0000041 0000041 00000025264 13704151031 021620 0 ustar www-data www-data # encoding: utf-8
require 'hocon/config_syntax'
require 'hocon/impl'
require 'hocon/impl/config_node_complex_value'
require 'hocon/impl/config_node_field'
require 'hocon/impl/config_node_single_token'
require 'hocon/impl/tokens'
class Hocon::Impl::ConfigNodeObject
include Hocon::Impl::ConfigNodeComplexValue
ConfigSyntax = Hocon::ConfigSyntax
Tokens = Hocon::Impl::Tokens
def new_node(nodes)
self.class.new(nodes)
end
def has_value(desired_path)
@children.each do |node|
if node.is_a?(Hocon::Impl::ConfigNodeField)
field = node
key = field.path.value
if key == desired_path || key.starts_with(desired_path)
return true
elsif desired_path.starts_with(key)
if field.value.is_a?(self.class)
obj = field.value
remaining_path = desired_path.sub_path_to_end(key.length)
if obj.has_value(remaining_path)
return true
end
end
end
end
end
false
end
def change_value_on_path(desired_path, value, flavor)
children_copy = @children.clone
seen_non_matching = false
# Copy the value so we can change it to null but not modify the original parameter
value_copy = value
i = children_copy.size
while i >= 0 do
if children_copy[i].is_a?(Hocon::Impl::ConfigNodeSingleToken)
t = children_copy[i].token
# Ensure that, when we are removing settings in JSON, we don't end up with a trailing comma
if flavor.equal?(ConfigSyntax::JSON) && !seen_non_matching && t.equal?(Tokens::COMMA)
children_copy.delete_at(i)
end
i -= 1
next
elsif !children_copy[i].is_a?(Hocon::Impl::ConfigNodeField)
i -= 1
next
end
node = children_copy[i]
key = node.path.value
# Delete all multi-element paths that start with the desired path, since technically they are duplicates
if (value_copy.nil? && key == desired_path) || (key.starts_with(desired_path) && !(key == desired_path))
children_copy.delete_at(i)
# Remove any whitespace or commas after the deleted setting
j = i
while j < children_copy.size
if children_copy[j].is_a?(Hocon::Impl::ConfigNodeSingleToken)
t = children_copy[j].token
if Tokens.ignored_whitespace?(t) || t.equal?(Tokens::COMMA)
children_copy.delete_at(j)
j -= 1
else
break
end
else
break
end
j += 1
end
elsif key == desired_path
seen_non_matching = true
before = i - 1 > 0 ? children_copy[i - 1] : nil
if value.is_a?(Hocon::Impl::ConfigNodeComplexValue) && before.is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
Tokens.ignored_whitespace?(before.token)
indented_value = value.indent_text(before)
else
indented_value = value
end
children_copy[i] = node.replace_value(indented_value)
value_copy = nil
elsif desired_path.starts_with(key)
seen_non_matching = true
if node.value.is_a?(self.class)
remaining_path = desired_path.sub_path_to_end(key.length)
children_copy[i] = node.replace_value(node.value.change_value_on_path(remaining_path, value_copy, flavor))
if !value_copy.nil? && !(node == @children[i])
value_copy = nil
end
end
else
seen_non_matching = true
end
i -= 1
end
self.class.new(children_copy)
end
def set_value_on_path(desired_path, value, flavor = ConfigSyntax::CONF)
path = Hocon::Impl::PathParser.parse_path_node(desired_path, flavor)
set_value_on_path_node(path, value, flavor)
end
def set_value_on_path_node(desired_path, value, flavor)
node = change_value_on_path(desired_path.value, value, flavor)
# If the desired Path did not exist, add it
unless node.has_value(desired_path.value)
return node.add_value_on_path(desired_path, value, flavor)
end
node
end
def indentation
seen_new_line = false
indentation = []
if @children.empty?
return indentation
end
@children.each_index do |i|
unless seen_new_line
if @children[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(@children[i].token)
seen_new_line = true
indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_line(nil)))
end
else
if @children[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
Tokens.ignored_whitespace?(@children[i].token) &&
i + 1 < @children.size &&
(@children[i + 1].is_a?(Hocon::Impl::ConfigNodeField) || @children[i + 1].is_a?(Hocon::Impl::ConfigNodeInclude))
# Return the indentation of the first setting on its own line
indentation.push(@children[i])
return indentation
end
end
end
if indentation.empty?
indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, " ")))
return indentation
else
# Calculate the indentation of the ending curly-brace to get the indentation of the root object
last = @children[-1]
if last.is_a?(Hocon::Impl::ConfigNodeSingleToken) && last.token.equal?(Tokens::CLOSE_CURLY)
beforeLast = @children[-2]
indent = ""
if beforeLast.is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
Tokens.ignored_whitespace?(beforeLast.token)
indent = beforeLast.token.token_text
end
indent += " "
indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, indent)))
return indentation
end
end
# The object has no curly braces and is at the root level, so don't indent
indentation
end
def add_value_on_path(desired_path, value, flavor)
path = desired_path.value
children_copy = @children.clone
indentation = indentation().clone
# If the value we're inserting is a complex value, we'll need to indent it for insertion
if value.is_a?(Hocon::Impl::ConfigNodeComplexValue) && indentation.length > 0
indented_value = value.indent_text(indentation[-1])
else
indented_value = value
end
same_line = !(indentation.length > 0 && indentation[0].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
Tokens.newline?(indentation[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
(0..@children.size - 1).reverse_each do |i|
unless @children[i].is_a?(Hocon::Impl::ConfigNodeField)
next
end
node = @children[i]
key = node.path.value
if path.starts_with(key) && node.value.is_a?(self.class)
remaining_path = desired_path.sub_path(key.length)
new_value = node.value
children_copy[i] = node.replace_value(new_value.add_value_on_path(remaining_path, value, flavor))
return self.class.new(children_copy)
end
end
end
# Otherwise, construct the new setting
starts_with_brace = @children[0].is_a?(Hocon::Impl::ConfigNodeSingleToken) && @children[0].token.equal?(Tokens::OPEN_CURLY)
new_nodes = []
new_nodes += indentation
new_nodes.push(desired_path.first)
new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::COLON))
new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, ' ')))
if path.length == 1
new_nodes.push(indented_value)
else
# If the path is of length greater than one add the required new objects along the path
new_object_nodes = []
new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::OPEN_CURLY))
if indentation.empty?
new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_line(nil)))
end
new_object_nodes += indentation
new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::CLOSE_CURLY))
new_object = self.class.new(new_object_nodes)
new_nodes.push(new_object.add_value_on_path(desired_path.sub_path(1), indented_value, flavor))
end
# Combine these two cases so that we only have to iterate once
if flavor.equal?(ConfigSyntax::JSON) || starts_with_brace || same_line
i = children_copy.size - 1
while i >= 0
# 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.equal?(ConfigSyntax::JSON) || same_line) && children_copy[i].is_a?(Hocon::Impl::ConfigNodeField)
if i + 1 >= children_copy.size ||
!(children_copy[i + 1].is_a?(Hocon::Impl::ConfigNodeSingleToken) && children_copy[i + 1].token.equal?(Tokens::COMMA))
children_copy.insert(i + 1, Hocon::Impl::ConfigNodeSingleToken.new(Tokens::COMMA))
break
end
end
# Add the value into the copy of the children map, keeping any whitespace/newlines
# before the close curly brace
if starts_with_brace && children_copy[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
children_copy[i].token == Tokens::CLOSE_CURLY
previous = children_copy[i - 1]
if previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(previous.token)
children_copy.insert(i - 1, Hocon::Impl::ConfigNodeField.new(new_nodes))
i -= 1
elsif previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.ignored_whitespace?(previous.token)
before_previous = children_copy[i - 2]
if same_line
children_copy.insert(i - 1, Hocon::Impl::ConfigNodeField.new(new_nodes))
i -= 1
elsif before_previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(before_previous.token)
children_copy.insert(i - 2, Hocon::Impl::ConfigNodeField.new(new_nodes))
i -= 2
else
children_copy.insert(i, Hocon::Impl::ConfigNodeField.new(new_nodes))
end
else
children_copy.insert(i, Hocon::Impl::ConfigNodeField.new(new_nodes))
end
end
i -= 1
end
end
unless starts_with_brace
if children_copy[-1].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(children_copy[-1].token)
children_copy.insert(-2, Hocon::Impl::ConfigNodeField.new(new_nodes))
else
children_copy.push(Hocon::Impl::ConfigNodeField.new(new_nodes))
end
end
self.class.new(children_copy)
end
def remove_value_on_path(desired_path, flavor)
path = Hocon::Impl::PathParser.parse_path_node(desired_path, flavor).value
change_value_on_path(path, nil, flavor)
end
end
hocon-1.3.1/lib/hocon/impl/config_parser.rb 0000644 0000041 0000041 00000032635 13704151031 020641 0 ustar www-data www-data # encoding: utf-8
require 'stringio'
require 'hocon/impl'
require 'hocon/impl/path_builder'
require 'hocon/config_syntax'
require 'hocon/impl/config_string'
require 'hocon/impl/config_concatenation'
require 'hocon/config_error'
require 'hocon/impl/simple_config_list'
require 'hocon/impl/simple_config_object'
require 'hocon/impl/path'
require 'hocon/impl/url'
require 'hocon/impl/config_reference'
require 'hocon/impl/substitution_expression'
require 'hocon/impl/config_node_simple_value'
require 'hocon/impl/config_node_object'
require 'hocon/impl/config_node_array'
require 'hocon/impl/config_node_concatenation'
require 'hocon/impl/config_include_kind'
class Hocon::Impl::ConfigParser
ConfigSyntax = Hocon::ConfigSyntax
ConfigConcatenation = Hocon::Impl::ConfigConcatenation
ConfigReference = Hocon::Impl::ConfigReference
ConfigParseError = Hocon::ConfigError::ConfigParseError
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
SimpleConfigObject = Hocon::Impl::SimpleConfigObject
SimpleConfigList = Hocon::Impl::SimpleConfigList
Path = Hocon::Impl::Path
ConfigIncludeKind = Hocon::Impl::ConfigIncludeKind
ConfigNodeInclude = Hocon::Impl::ConfigNodeInclude
ConfigNodeComment = Hocon::Impl::ConfigNodeComment
ConfigNodeSingleToken = Hocon::Impl::ConfigNodeSingleToken
Tokens = Hocon::Impl::Tokens
def self.parse(document, origin, options, include_context)
context = Hocon::Impl::ConfigParser::ParseContext.new(
options.syntax, origin, document,
Hocon::Impl::SimpleIncluder.make_full(options.includer),
include_context)
context.parse
end
class ParseContext
def initialize(flavor, origin, document, includer, include_context)
@line_number = 1
@document = document
@flavor = flavor
@base_origin = origin
@includer = includer
@include_context = include_context
@path_stack = []
@array_count = 0
end
# merge a bunch of adjacent values into one
# value; change unquoted text into a string
# value.
def parse_concatenation(n)
# this trick is not done in JSON
if @flavor.equal?(ConfigSyntax::JSON)
raise ConfigBugOrBrokenError, "Found a concatenation node in JSON"
end
values = []
n.children.each do |node|
if node.is_a?(Hocon::Impl::AbstractConfigNodeValue)
v = parse_value(node, nil)
values.push(v)
end
end
ConfigConcatenation.concatenate(values)
end
def line_origin
@base_origin.with_line_number(@line_number)
end
def parse_error(message, cause = nil)
ConfigParseError.new(line_origin, message, cause)
end
def full_current_path
# pathStack has top of stack at front
if @path_stack.empty?
raise ConfigBugOrBrokenError, "Bug in parser; tried to get current path when at root"
else
Path.from_path_list(@path_stack.reverse)
end
end
def parse_value(n, comments)
starting_array_count = @array_count
if n.is_a?(Hocon::Impl::ConfigNodeSimpleValue)
v = n.value
elsif n.is_a?(Hocon::Impl::ConfigNodeObject)
v = parse_object(n)
elsif n.is_a?(Hocon::Impl::ConfigNodeArray)
v = parse_array(n)
elsif n.is_a?(Hocon::Impl::ConfigNodeConcatenation)
v = parse_concatenation(n)
else
raise parse_error("Expecting a value but got wrong node type: #{n.class}")
end
unless comments.nil? || comments.empty?
v = v.with_origin(v.origin.prepend_comments(comments.clone))
comments.clear
end
unless @array_count == starting_array_count
raise ConfigBugOrBrokenError, "Bug in config parser: unbalanced array count"
end
v
end
def create_value_under_path(path, value)
# for path foo.bar, we are creating
# { "foo" : { "bar" : value } }
keys = []
key = path.first
remaining = path.remainder
until key.nil?
keys.push(key)
if remaining.nil?
break
else
key = remaining.first
remaining = remaining.remainder
end
end
# the setComments(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"
keys = keys.reverse
# this is just a ruby means for doing first/rest
deepest, *rest = *keys
o = SimpleConfigObject.new(value.origin.with_comments(nil),
{deepest => value})
while !rest.empty?
deepest, *rest = *rest
o = SimpleConfigObject.new(value.origin.with_comments(nil),
{deepest => o})
end
o
end
def parse_include(values, n)
case n.kind
when ConfigIncludeKind::URL
url = nil
begin
url = Hocon::Impl::Url.new(n.name)
rescue Hocon::Impl::Url::MalformedUrlError => e
raise parse_error("include url() specifies an invalid URL: #{n.name}", e)
end
obj = @includer.include_url(@include_context, url)
when ConfigIncludeKind::FILE
obj = @includer.include_file(@include_context, n.name)
when ConfigIncludeKind::CLASSPATH
obj = @includer.include_resources(@include_context, n.name)
when ConfigIncludeKind::HEURISTIC
obj = @includer.include(@include_context, n.name)
else
raise ConfigBugOrBrokenError, "should not be reached"
end
# we really should make this work, but for now throwing an
# exception is better than producing an incorrect result.
# See https://github.com/typesafehub/config/issues/160
if @array_count > 0 && (obj.resolve_status != Hocon::Impl::ResolveStatus::RESOLVED)
raise parse_error("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.")
end
if !(@path_stack.empty?)
prefix = full_current_path
obj = obj.relativized(prefix)
end
obj.key_set.each do |key|
v = obj.get(key)
existing = values[key]
if !(existing.nil?)
values[key] = v.with_fallback(existing)
else
values[key] = v
end
end
end
def parse_object(n)
values = Hash.new
object_origin = line_origin
last_was_new_line = false
nodes = n.children.clone
comments = []
i = 0
while i < nodes.size
node = nodes[i]
if node.is_a?(ConfigNodeComment)
last_was_new_line = false
comments.push(node.comment_text)
elsif node.is_a?(ConfigNodeSingleToken) && Tokens.newline?(node.token)
@line_number += 1
if last_was_new_line
# Drop all comments if there was a blank line and start a new comment block
comments.clear
end
last_was_new_line = true
elsif !@flavor.equal?(ConfigSyntax::JSON) && node.is_a?(ConfigNodeInclude)
parse_include(values, node)
last_was_new_line = false
elsif node.is_a?(Hocon::Impl::ConfigNodeField)
last_was_new_line = false
path = node.path.value
comments += node.comments
# path must be on-stack while we parse the value
# Note that, in upstream, pathStack is a LinkedList, so use unshift instead of push
@path_stack.unshift(path)
if node.separator.equal?(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/typesafehub/config/issues/160
if @array_count > 0
raise parse_error("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 ${}.")
end
# 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.
@array_count += 1
end
value_node = node.value
# comments from the key token go to the value token
new_value = parse_value(value_node, comments)
if node.separator.equal?(Tokens::PLUS_EQUALS)
@array_count -= 1
concat = []
previous_ref = ConfigReference.new(new_value.origin,
Hocon::Impl::SubstitutionExpression.new(full_current_path, true))
list = SimpleConfigList.new(new_value.origin, [new_value])
concat << previous_ref
concat << list
new_value = ConfigConcatenation.concatenate(concat)
end
# Grab any trailing comments on the same line
if i < nodes.size - 1
i += 1
while i < nodes.size
if nodes[i].is_a?(ConfigNodeComment)
comment = nodes[i]
new_value = new_value.with_origin(new_value.origin.append_comments([comment.comment_text]))
break
elsif nodes[i].is_a?(ConfigNodeSingleToken)
curr = nodes[i]
if curr.token.equal?(Tokens::COMMA) || Tokens.ignored_whitespace?(curr.token)
# keep searching, as there could still be a comment
else
i -= 1
break
end
else
i -= 1
break
end
i += 1
end
end
@path_stack.shift
key = path.first
remaining = path.remainder
if remaining.nil?
existing = values[key]
unless existing.nil?
# 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.equal?(ConfigSyntax::JSON)
raise parse_error("JSON does not allow duplicate fields: '#{key}'" +
" was already seen at #{existing.origin().description()}")
else
new_value = new_value.with_fallback(existing)
end
end
values[key] = new_value
else
if @flavor == ConfigSyntax::JSON
raise Hocon::ConfigError::ConfigBugOrBrokenError, "somehow got multi-element path in JSON mode"
end
obj = create_value_under_path(remaining, new_value)
existing = values[key]
if !existing.nil?
obj = obj.with_fallback(existing)
end
values[key] = obj
end
end
i += 1
end
SimpleConfigObject.new(object_origin, values)
end
def parse_array(n)
@array_count += 1
array_origin = line_origin
values = []
last_was_new_line = false
comments = []
v = nil
n.children.each do |node|
if node.is_a?(ConfigNodeComment)
comments << node.comment_text
last_was_new_line = false
elsif node.is_a?(ConfigNodeSingleToken) && Tokens.newline?(node.token)
@line_number += 1
if last_was_new_line && v.nil?
comments.clear
elsif !v.nil?
values << v.with_origin(v.origin.append_comments(comments.clone))
comments.clear
v = nil
end
last_was_new_line = true
elsif node.is_a?(Hocon::Impl::AbstractConfigNodeValue)
last_was_new_line = false
unless v.nil?
values << v.with_origin(v.origin.append_comments(comments.clone))
comments.clear
end
v = parse_value(node, comments)
end
end
# There shouldn't be any comments at this point, but add them just in case
unless v.nil?
values << v.with_origin(v.origin.append_comments(comments.clone))
end
@array_count -= 1
SimpleConfigList.new(array_origin, values)
end
def parse
result = nil
comments = []
last_was_new_line = false
@document.children.each do |node|
if node.is_a?(ConfigNodeComment)
comments << node.comment_text
last_was_new_line = false
elsif node.is_a?(ConfigNodeSingleToken)
t = node.token
if Tokens.newline?(t)
@line_number += 1
if last_was_new_line && result.nil?
comments.clear
elsif !result.nil?
result = result.with_origin(result.origin.append_comments(comments.clone))
comments.clear
break
end
last_was_new_line = true
end
elsif node.is_a?(Hocon::Impl::ConfigNodeComplexValue)
result = parse_value(node, comments)
last_was_new_line = false
end
end
result
end
end
end
hocon-1.3.1/lib/hocon/impl/token_type.rb 0000644 0000041 0000041 00000002163 13704151031 020172 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
class Hocon::Impl::TokenType
START = 0
EOF = 1
COMMA = 2
EQUALS = 3
COLON = 4
OPEN_CURLY = 5
CLOSE_CURLY = 6
OPEN_SQUARE = 7
CLOSE_SQUARE = 8
VALUE = 9
NEWLINE = 10
UNQUOTED_TEXT = 11
SUBSTITUTION = 12
PROBLEM = 13
COMMENT = 14
PLUS_EQUALS = 15
IGNORED_WHITESPACE = 16
def self.token_type_name(token_type)
case token_type
when START then "START"
when EOF then "EOF"
when COMMA then "COMMA"
when EQUALS then "EQUALS"
when COLON then "COLON"
when OPEN_CURLY then "OPEN_CURLY"
when CLOSE_CURLY then "CLOSE_CURLY"
when OPEN_SQUARE then "OPEN_SQUARE"
when CLOSE_SQUARE then "CLOSE_SQUARE"
when VALUE then "VALUE"
when NEWLINE then "NEWLINE"
when UNQUOTED_TEXT then "UNQUOTED_TEXT"
when SUBSTITUTION then "SUBSTITUTION"
when PROBLEM then "PROBLEM"
when COMMENT then "COMMENT"
when PLUS_EQUALS then "PLUS_EQUALS"
when IGNORED_WHITESPACE then "IGNORED_WHITESPACE"
else raise ConfigBugOrBrokenError, "Unrecognized token type #{token_type}"
end
end
end
hocon-1.3.1/lib/hocon/impl/config_concatenation.rb 0000644 0000041 0000041 00000020746 13704151031 022172 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/abstract_config_value'
require 'hocon/impl/abstract_config_object'
require 'hocon/impl/simple_config_list'
require 'hocon/config_object'
require 'hocon/impl/unmergeable'
require 'hocon/impl/simple_config_origin'
require 'hocon/impl/config_string'
require 'hocon/impl/container'
class Hocon::Impl::ConfigConcatenation
include Hocon::Impl::Unmergeable
include Hocon::Impl::Container
include Hocon::Impl::AbstractConfigValue
SimpleConfigList = Hocon::Impl::SimpleConfigList
ConfigObject = Hocon::ConfigObject
ConfigString = Hocon::Impl::ConfigString
ResolveStatus = Hocon::Impl::ResolveStatus
Unmergeable = Hocon::Impl::Unmergeable
SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError
ConfigWrongTypeError = Hocon::ConfigError::ConfigWrongTypeError
attr_reader :pieces
def initialize(origin, pieces)
super(origin)
@pieces = pieces
if pieces.size < 2
raise ConfigBugOrBrokenError, "Created concatenation with less than 2 items: #{self}"
end
had_unmergeable = false
pieces.each do |p|
if p.is_a?(Hocon::Impl::ConfigConcatenation)
raise ConfigBugOrBrokenError, "ConfigConcatenation should never be nested: #{self}"
end
if p.is_a?(Unmergeable)
had_unmergeable = true
end
end
unless had_unmergeable
raise ConfigBugOrBrokenError, "Created concatenation without an unmergeable in it: #{self}"
end
end
def value_type
raise not_resolved
end
def unwrapped
raise not_resolved
end
def new_copy(new_origin)
self.class.new(new_origin, @pieces)
end
def ignores_fallbacks?
# 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.
false
end
def unmerged_values
[self]
end
#
# Add left and right, or their merger, to builder
#
def self.join(builder, orig_right)
left = builder[builder.size - 1]
right = orig_right
# 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.is_a?(ConfigObject)) && (right.is_a?(SimpleConfigList))
left = Hocon::Impl::DefaultTransformer.transform(left, Hocon::ConfigValueType::LIST)
elsif (left.is_a?(SimpleConfigList)) && (right.is_a?(ConfigObject))
right = Hocon::Impl::DefaultTransformer.transform(right, Hocon::ConfigValueType::LIST)
end
# 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.
joined = nil
if (left.is_a?(ConfigObject)) && (right.is_a?(ConfigObject))
joined = right.with_fallback(left)
elsif (left.is_a?(SimpleConfigList)) && (right.is_a?(SimpleConfigList))
joined = left.concatenate(right)
elsif (left.is_a?(SimpleConfigList) || left.is_a?(ConfigObject)) &&
is_ignored_whitespace(right)
joined = left
# it should be impossible that left is whitespace and right is a list or object
elsif (left.is_a?(Hocon::Impl::ConfigConcatenation)) ||
(right.is_a?(Hocon::Impl::ConfigConcatenation))
raise ConfigBugOrBrokenError, "unflattened ConfigConcatenation"
elsif (left.is_a?(Unmergeable)) || (right.is_a?(Unmergeable))
# leave joined=null, cannot join
else
# handle primitive type or primitive type mixed with object or list
s1 = left.transform_to_string
s2 = right.transform_to_string
if s1.nil? || s2.nil?
raise ConfigWrongTypeError.new(left.origin,
"Cannot concatenate object or list with a non-object-or-list, #{left} " +
"and #{right} are not compatible", nil)
else
joined_origin = SimpleConfigOrigin.merge_origins([left.origin, right.origin])
joined = Hocon::Impl::ConfigString::Quoted.new(joined_origin, s1 + s2)
end
end
if joined.nil?
builder.push(right)
else
builder.pop
builder.push(joined)
end
end
def self.consolidate(pieces)
if pieces.length < 2
pieces
else
flattened = []
pieces.each do |v|
if v.is_a?(Hocon::Impl::ConfigConcatenation)
flattened.concat(v.pieces)
else
flattened.push(v)
end
end
consolidated = []
flattened.each do |v|
if consolidated.empty?
consolidated.push(v)
else
join(consolidated, v)
end
end
consolidated
end
end
def self.concatenate(pieces)
consolidated = consolidate(pieces)
if consolidated.empty?
nil
elsif consolidated.length == 1
consolidated[0]
else
merged_origin = SimpleConfigOrigin.merge_value_origins(consolidated)
Hocon::Impl::ConfigConcatenation.new(merged_origin, consolidated)
end
end
def resolve_substitutions(context, source)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
indent = context.depth + 2
Hocon::Impl::ConfigImpl.trace("concatenation has #{@pieces.size} pieces",
indent - 1)
count = 0
@pieces.each { |v|
Hocon::Impl::ConfigImpl.trace("#{count}: #{v}", count)
count += 1
}
end
# 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.
source_with_parent = source
new_context = context
resolved = []
@pieces.each { |p|
# to concat into a string we have to do a full resolve,
# so unrestrict the context, then put restriction back afterward
restriction = new_context.restrict_to_child
result = new_context.unrestricted
.resolve(p, source_with_parent)
r = result.value
new_context = result.context.restrict(restriction)
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
Hocon::Impl::ConfigImpl.trace("resolved concat piece to #{r}",
context.depth)
end
if r
resolved << r
end
# otherwise, it was optional ... omit
}
# now need to concat everything
joined = self.class.consolidate(resolved)
# if unresolved is allowed we can just become another
# ConfigConcatenation
if joined.size > 1 and context.options.allow_unresolved
Hocon::Impl::ResolveResult.make(new_context, Hocon::Impl::ConfigConcatenation.new(origin, joined))
elsif joined.empty?
# we had just a list of optional references using ${?}
Hocon::Impl::ResolveResult.make(new_context, nil)
elsif joined.size == 1
Hocon::Impl::ResolveResult.make(new_context, joined[0])
else
raise ConfigBugOrBrokenError.new(
"Bug in the library; resolved list was joined to too many values: #{joined}")
end
end
def resolve_status
ResolveStatus::UNRESOLVED
end
def replace_child(child, replacement)
new_pieces = replace_child_in_list(@pieces, child, replacement)
if new_pieces == nil
nil
else
self.class.new(origin, new_pieces)
end
end
def has_descendant?(descendant)
has_descendant_in_list?(@pieces, descendant)
end
# 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.
def relativized(prefix)
new_pieces = []
@pieces.each { |p|
new_pieces << p.relativized(prefix)
}
self.class.new(origin, new_pieces)
end
def can_equal(other)
other.is_a? Hocon::Impl::ConfigConcatenation
end
def ==(other)
if other.is_a? Hocon::Impl::ConfigConcatenation
can_equal(other) && @pieces == other.pieces
else
false
end
end
def hash
# note that "origin" is deliberately NOT part of equality
@pieces.hash
end
def render_value_to_sb(sb, indent, at_root, options)
@pieces.each do |piece|
piece.render_value_to_sb(sb, indent, at_root, options)
end
end
private
def not_resolved
ConfigNotResolvedError.new("need to Config#resolve(), see the API docs for Config#resolve(); substitution not resolved: #{self}")
end
def self.is_ignored_whitespace(value)
return value.is_a?(ConfigString) && !value.was_quoted?
end
end
hocon-1.3.1/lib/hocon/impl/unsupported_operation_error.rb 0000644 0000041 0000041 00000000152 13704151031 023666 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
class Hocon::Impl::UnsupportedOperationError < StandardError
end
hocon-1.3.1/lib/hocon/impl/path.rb 0000644 0000041 0000041 00000013111 13704151031 016740 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/path_builder'
require 'hocon/config_error'
require 'stringio'
class Hocon::Impl::Path
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
ConfigImplUtil = Hocon::Impl::ConfigImplUtil
def initialize(first, remainder)
# first: String, remainder: Path
@first = first
@remainder = remainder
end
attr_reader :first, :remainder
def self.from_string_list(elements)
# This method was translated from the Path constructor in the
# Java hocon library that has this signature:
# Path(String... elements)
#
# It figures out what @first and @remainder should be, then
# pass those to the ruby constructor
if elements.length == 0
raise Hocon::ConfigError::ConfigBugOrBrokenError.new("empty path")
end
new_first = elements.first
if elements.length > 1
pb = Hocon::Impl::PathBuilder.new
# Skip first element
elements.drop(1).each do |element|
pb.append_key(element)
end
new_remainder = pb.result
else
new_remainder = nil
end
self.new(new_first, new_remainder)
end
def self.from_path_list(path_list)
# This method was translated from the Path constructors in the
# Java hocon library that take in a list of Paths
#
# It just passes an iterator to self.from_path_iterator, which
# will return a new Path object
from_path_iterator(path_list.each)
end
def self.from_path_iterator(path_iterator)
# This method was translated from the Path constructors in the
# Java hocon library that takes in an iterator of Paths
#
# It figures out what @first and @remainder should be, then
# pass those to the ruby constructor
# Try to get first path from iterator
# Ruby iterators have no .hasNext() method like java
# So we try to catch the StopIteration exception
begin
first_path = path_iterator.next
rescue StopIteration
raise Hocon::ConfigError::ConfigBugOrBrokenError("empty path")
end
new_first = first_path.first
pb = Hocon::Impl::PathBuilder.new
unless first_path.remainder.nil?
pb.append_path(first_path.remainder)
end
# Skip first path
path_iterator.drop(1).each do |path|
pb.append_path(path)
end
new_remainder = pb.result
self.new(new_first, new_remainder)
end
def first
@first
end
def remainder
@remainder
end
def parent
if remainder.nil?
return nil
end
pb = Hocon::Impl::PathBuilder.new
p = self
while not p.remainder.nil?
pb.append_key(p.first)
p = p.remainder
end
pb.result
end
def last
p = self
while p.remainder != nil
p = p.remainder
end
p.first
end
def prepend(to_prepend)
pb = Hocon::Impl::PathBuilder.new
pb.append_path(to_prepend)
pb.append_path(self)
pb.result
end
def length
count = 1
p = remainder
while p != nil do
count += 1
p = p.remainder
end
count
end
def sub_path(first_index, last_index)
if last_index < first_index
raise ConfigBugOrBrokenError.new("bad call to sub_path")
end
from = sub_path_to_end(first_index)
pb = Hocon::Impl::PathBuilder.new
count = last_index - first_index
while count > 0 do
count -= 1
pb.append_key(from.first)
from = from.remainder
if from.nil?
raise ConfigBugOrBrokenError.new("sub_path last_index out of range #{last_index}")
end
end
pb.result
end
# translated from `subPath(int removeFromFront)` upstream
def sub_path_to_end(remove_from_front)
count = remove_from_front
p = self
while (not p.nil?) && count > 0 do
count -= 1
p = p.remainder
end
p
end
def starts_with(other)
my_remainder = self
other_remainder = other
if other_remainder.length <= my_remainder.length
while ! other_remainder.nil?
if ! (other_remainder.first == my_remainder.first)
return false
end
my_remainder = my_remainder.remainder
other_remainder = other_remainder.remainder
end
return true
end
false
end
def ==(other)
if other.is_a? Hocon::Impl::Path
that = other
first == that.first && ConfigImplUtil.equals_handling_nil?(remainder, that.remainder)
else
false
end
end
def hash
remainder_hash = remainder.nil? ? 0 : remainder.hash
41 * (41 + first.hash) + remainder_hash
end
# this doesn't have a very precise meaning, just to reduce
# noise from quotes in the rendered path for average cases
def self.has_funky_chars?(s)
length = s.length
if length == 0
return false
end
s.chars.each do |c|
unless (c =~ /[[:alnum:]]/) || (c == '-') || (c == '_')
return true
end
end
false
end
def append_to_string_builder(sb)
if self.class.has_funky_chars?(@first) || @first.empty?
sb << ConfigImplUtil.render_json_string(@first)
else
sb << @first
end
unless @remainder.nil?
sb << "."
@remainder.append_to_string_builder(sb)
end
end
def to_s
sb = StringIO.new
sb << "Path("
append_to_string_builder(sb)
sb << ")"
sb.string
end
def inspect
to_s
end
#
# toString() is a debugging-oriented version while this is an
# error-message-oriented human-readable one.
#
def render
sb = StringIO.new
append_to_string_builder(sb)
sb.string
end
def self.new_key(key)
return self.new(key, nil)
end
def self.new_path(path)
Hocon::Impl::PathParser.parse_path(path)
end
end
hocon-1.3.1/lib/hocon/impl/simple_config_origin.rb 0000644 0000041 0000041 00000023315 13704151031 022200 0 ustar www-data www-data # encoding: utf-8
require 'uri'
require 'hocon/impl'
require 'hocon/impl/url'
require 'hocon/impl/origin_type'
require 'hocon/config_error'
class Hocon::Impl::SimpleConfigOrigin
OriginType = Hocon::Impl::OriginType
def initialize(description, line_number, end_line_number,
origin_type, url_or_nil, resource_or_nil, comments_or_nil)
if !description
raise ArgumentError, "description may not be nil"
end
# HACK: naming this variable with an underscore, because the upstream library
# has both a member var and a no-arg method by the name "description", and
# I don't think Ruby can handle that.
@_description = description
@line_number = line_number
@end_line_number = end_line_number
@origin_type = origin_type
# TODO: Currently ruby hocon does not support URLs, and so this variable
# is not actually a URL/URI, but a string
@url_or_nil = url_or_nil
@resource_or_nil = resource_or_nil
@comments_or_nil = comments_or_nil
end
attr_reader :_description, :line_number, :end_line_number, :origin_type,
:url_or_nil, :resource_or_nil, :comments_or_nil
def self.new_simple(description)
self.new(description, -1, -1,
OriginType::GENERIC,
nil, nil, nil)
end
def self.new_file(file_path)
self.new(file_path, -1, -1,
OriginType::FILE,
file_path, nil, nil)
end
# NOTE: not porting `new_url` because we're not going to support URLs for now
def self.new_resource(resource, url = nil)
desc = nil
if ! url.nil?
desc = resource + " @ " + url.to_external_form
else
desc = resource
end
Hocon::Impl::SimpleConfigOrigin.new(desc, -1, -1, OriginType::RESOURCE,
url.nil? ? nil : url.to_external_form,
resource, nil)
end
def with_line_number(line_number)
if (line_number == @line_number) and
(line_number == @end_line_number)
self
else
Hocon::Impl::SimpleConfigOrigin.new(
@_description, line_number, line_number,
@origin_type, @url_or_nil, @resource_or_nil, @comments_or_nil)
end
end
def add_url(url)
SimpleConfigOrigin.new(@_description, line_number, end_line_number, origin_type,
url.nil? ? nil : url.to_s, resource_or_nil,
comments_or_nil)
end
def with_comments(comments)
if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(comments, @comments_or_nil)
self
else
Hocon::Impl::SimpleConfigOrigin.new(
@_description, @line_number, @end_line_number,
@origin_type, @url_or_nil, @resource_or_nil, comments)
end
end
def prepend_comments(comments)
if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(comments, @comments_or_nil)
self
elsif @comments_or_nil.nil?
with_comments(comments)
else
merged = comments + @comments_or_nil
with_comments(merged)
end
end
def append_comments(comments)
if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(comments, @comments_or_nil)
self
elsif comments_or_nil.nil?
with_comments(comments)
else
merged = comments_or_nil + comments
with_comments(merged)
end
end
def description
if @line_number < 0
_description
elsif end_line_number == line_number
"#{_description}: #{line_number}"
else
"#{_description}: #{line_number}-#{end_line_number}"
end
end
def ==(other)
if other.is_a? Hocon::Impl::SimpleConfigOrigin
@_description == other._description &&
@line_number == other.line_number &&
@end_line_number == other.end_line_number &&
@origin_type == other.origin_type &&
Hocon::Impl::ConfigImplUtil.equals_handling_nil?(@url_or_nil, other.url_or_nil) &&
Hocon::Impl::ConfigImplUtil.equals_handling_nil?(@resource_or_nil, other.resource_or_nil)
else
false
end
end
def hash
h = 41 * (41 + @_description.hash)
h = 41 * (h + @line_number)
h = 41 * (h + @end_line_number)
h = 41 * (h + @origin_type.hash)
unless @url_or_nil.nil?
h = 41 * (h + @url_or_nil.hash)
end
unless @resource_or_nil.nil?
h = 41 * (h + @resource_or_nil.hash)
end
h
end
def to_s
"ConfigOrigin(#{_description})"
end
def filename
# TODO because we don't support URLs, this function's URL handling
# is completely pointless. It will only return _description (a string that
# is the file path) or nil.
# It should be cleaned up
if origin_type == OriginType::FILE
_description
elsif ! url_or_nil.nil?
url = nil
begin
url = Hocon::Impl::Url.new(url_or_nil)
rescue Hocon::Impl::Url::MalformedUrlError => e
return nil
end
if url.get_protocol == "file"
url.get_file
else
nil
end
else
nil
end
end
def url
if url_or_nil.nil?
nil
else
begin
Hocon::Impl::Url.new(url_or_nil)
rescue Hocon::Impl::Url::MalformedUrlError => e
nil
end
end
end
def resource
resource_or_nil
end
def comments
@comments_or_nil || []
end
MERGE_OF_PREFIX = "merge of "
def self.remove_merge_of_prefix(desc)
if desc.start_with?(MERGE_OF_PREFIX)
desc = desc[MERGE_OF_PREFIX.length, desc.length - 1]
end
desc
end
def self.merge_two(a, b)
merged_desc = nil
merged_start_line = nil
merged_end_line = nil
merged_comments = nil
merged_type =
if a.origin_type == b.origin_type
a.origin_type
else
Hocon::Impl::OriginType::GENERIC
end
# first use the "description" field which has no line numbers
# cluttering it.
a_desc = remove_merge_of_prefix(a._description)
b_desc = remove_merge_of_prefix(b._description)
if a_desc == b_desc
merged_desc = a_desc
if a.line_number < 0
merged_start_line = b.line_number
elsif b.line_number < 0
merged_start_line = a.line_number
else
merged_start_line = [a.line_number, b.line_number].min
end
merged_end_line = [a.end_line_number, b.end_line_number].max
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.
a_full = remove_merge_of_prefix(a._description)
b_full = remove_merge_of_prefix(b._description)
merged_desc = "#{MERGE_OF_PREFIX}#{a_full},#{b_full}"
merged_start_line = -1
merged_end_line = -1
end
merged_url =
if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.url_or_nil, b.url_or_nil)
a.url_or_nil
else
nil
end
merged_resource =
if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.resource_or_nil, b.resource_or_nil)
a.resource_or_nil
else
nil
end
if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.comments_or_nil, b.comments_or_nil)
merged_comments = a.comments_or_nil
else
merged_comments = []
if a.comments_or_nil
merged_comments.concat(a.comments_or_nil)
end
if b.comments_or_nil
merged_comments.concat(b.comments_or_nil)
end
end
Hocon::Impl::SimpleConfigOrigin.new(
merged_desc, merged_start_line, merged_end_line,
merged_type, merged_url, merged_resource, merged_comments)
end
def self.similarity(a, b)
count = 0
if a.origin_type == b.origin_type
count += 1
end
if a._description == b._description
count += 1
# only count these if the description field (which is the file
# or resource name) also matches.
if a.line_number == b.line_number
count += 1
end
if a.end_line_number == b.end_line_number
count += 1
end
if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.url_or_nil, b.url_or_nil)
count += 1
end
if Hocon::Impl::ConfigImplUtil.equals_handling_nil?(a.resource_or_nil, b.resource_or_nil)
count += 1
end
end
count
end
def self.merge_three(a, b, c)
if similarity(a, b) >= similarity(b, c)
merge_two(merge_two(a, b), c)
else
merge_two(a, merge_two(b, c))
end
end
def self.merge_two_origins(a, b)
# a, b are ConfigOrigins
merge_two(a, b)
end
def self.merge_value_origins(stack)
# stack is an array of AbstractConfigValue
origins = stack.map { |v| v.origin}
merge_origins(origins)
end
# see also 'merge_two_origins' and 'merge_three_origins'
def self.merge_origins(stack)
# stack is an array of ConfigOrigin
if stack.empty?
raise Hocon::ConfigError::ConfigBugOrBrokenError, "can't merge empty list of origins"
elsif stack.length == 1
stack[0]
elsif stack.length == 2
merge_two(stack[0], stack[1])
else
remaining = []
stack.each do |o|
remaining << o
end
while remaining.size > 2
c = remaining.last
remaining.delete_at(remaining.size - 1)
b = remaining.last
remaining.delete_at(remaining.size - 1)
a = remaining.last
remaining.delete_at(remaining.size - 1)
merged = merge_three(a, b, c)
remaining << merged
end
# should be down to either 1 or 2
self.merge_origins(remaining)
end
end
# NOTE: skipping 'toFields', 'toFieldsDelta', 'fieldsDelta', 'fromFields',
# 'applyFieldsDelta', and 'fromBase' from upstream for now, because they appear
# to be about serialization and we probably won't be supporting that.
end
hocon-1.3.1/lib/hocon/impl/default_transformer.rb 0000644 0000041 0000041 00000007066 13704151031 022066 0 ustar www-data www-data # encoding: utf-8
require 'hocon/impl'
require 'hocon/impl/config_string'
require 'hocon/config_value_type'
require 'hocon/impl/config_boolean'
class Hocon::Impl::DefaultTransformer
ConfigValueType = Hocon::ConfigValueType
ConfigString = Hocon::Impl::ConfigString
ConfigBoolean = Hocon::Impl::ConfigBoolean
def self.transform(value, requested)
if value.value_type == ConfigValueType::STRING
s = value.unwrapped
case requested
when ConfigValueType::NUMBER
begin
v = Integer(s)
return ConfigInt.new(value.origin, v, s)
rescue ArgumentError
# try Float
end
begin
v = Float(s)
return ConfigFloat.new(value.origin, v, s)
rescue ArgumentError
# oh well.
end
when ConfigValueType::NULL
if s == "null"
return ConfigNull.new(value.origin)
end
when ConfigValueType::BOOLEAN
if s == "true" || s == "yes" || s == "on"
return ConfigBoolean.new(value.origin, true)
elsif s == "false" || s == "no" || s == "off"
return ConfigBoolean.new(value.origin, false)
end
when ConfigValueType::LIST
# can't go STRING to LIST automatically
when ConfigValueType::OBJECT
# can't go STRING to OBJECT automatically
when ConfigValueType::STRING
# no-op STRING to STRING
end
elsif requested == 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.
case value.value_type
# Ruby note: can't fall through in ruby. In the java code, NUMBER
# just rolls over to the BOOLEAN case
when ConfigValueType::NUMBER
return ConfigString::Quoted.new(value.origin, value.transform_to_string)
when ConfigValueType::BOOLEAN
return ConfigString::Quoted.new(value.origin, value.transform_to_string)
when ConfigValueType::NULL
# want to be sure this throws instead of returning "null" as a
# string
when ConfigValueType::OBJECT
# no OBJECT to STRING automatically
when ConfigValueType::LIST
# no LIST to STRING automatically
when ConfigValueType::STRING
# no-op STRING to STRING
end
elsif requested == ConfigValueType::LIST && value.value_type == 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 :-/
o = value
values = Hash.new
values
o.keys.each do |key|
begin
i = Integer(key, 10)
if i < 0
next
end
values[key] = i
rescue ArgumentError
next
end
end
if not values.empty?
entry_list = values.to_a
# sort by numeric index
entry_list.sort! {|a,b| b[0] <=> a[0]}
# drop the indices (we allow gaps in the indices, for better or
# worse)
list = Array.new
entry_list.each do |entry|
list.push(entry[1])
end
return SimpleConfigList.new(value.origin, list)
end
end
value
end
end
hocon-1.3.1/lib/hocon/version.rb 0000644 0000041 0000041 00000000075 13704151031 016535 0 ustar www-data www-data module Hocon
module Version
STRING = '1.3.1'
end
end
hocon-1.3.1/lib/hocon/config_factory.rb 0000644 0000041 0000041 00000004374 13704151031 020052 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/impl/parseable'
require 'hocon/config_parse_options'
require 'hocon/impl/config_impl'
require 'hocon/config_factory'
## Please note that the `parse` operations will simply create a ConfigValue
## and do nothing else, whereas the `load` operations will perform a higher-level
## operation and will resolve substitutions. If you have substitutions in your
## configuration, use a `load` function
class Hocon::ConfigFactory
def self.parse_file(file_path, options = Hocon::ConfigParseOptions.defaults)
Hocon::Impl::Parseable.new_file(file_path, options).parse.to_config
end
def self.parse_string(string, options = Hocon::ConfigParseOptions.defaults)
Hocon::Impl::Parseable.new_string(string, options).parse.to_config
end
def self.parse_file_any_syntax(file_base_name, options)
Hocon::Impl::ConfigImpl.parse_file_any_syntax(file_base_name, options).to_config
end
def self.empty(origin_description = nil)
Hocon::Impl::ConfigImpl.empty_config(origin_description)
end
# Because of how optional arguments work, if either parse or resolve options is supplied
# both must be supplied. load_file_with_parse_options or load_file_with_resolve_options
# can be used instead, or the argument you don't care about in load_file can be nil
#
# e.g.:
# load_file("settings", my_parse_options, nil)
# is equivalent to:
# load_file_with_parse_options("settings", my_parse_options)
def self.load_file(file_base_name, parse_options = nil, resolve_options = nil)
parse_options ||= Hocon::ConfigParseOptions.defaults
resolve_options ||= Hocon::ConfigResolveOptions.defaults
config = Hocon::ConfigFactory.parse_file_any_syntax(file_base_name, parse_options)
self.load_from_config(config, resolve_options)
end
def self.load_file_with_parse_options(file_base_name, parse_options)
self.load_file(file_base_name, parse_options, nil)
end
def self.load_file_with_resolve_options(file_base_name, resolve_options)
self.load_file(file_base_name, nil, resolve_options)
end
def self.load_from_config(config, resolve_options)
config.with_fallback(self.default_reference).resolve(resolve_options)
end
def self.default_reference
Hocon::Impl::ConfigImpl.default_reference
end
end
hocon-1.3.1/lib/hocon/impl.rb 0000644 0000041 0000041 00000000073 13704151031 016007 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
module Hocon::Impl
end hocon-1.3.1/lib/hocon/config_include_context.rb 0000644 0000041 0000041 00000003504 13704151031 021564 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_error'
#
# 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.
#
module Hocon::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
#
def relative_to(filename)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigIncludeContext` must implement `relative_to` (#{self.class})"
end
#
# Parse options to use (if you use another method to get a
# {@link ConfigParseable} then use {@link ConfigParseable#options()}
# instead though).
#
# @return the parse options
#
def parse_options
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigIncludeContext` must implement `parse_options` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/config_syntax.rb 0000644 0000041 0000041 00000000414 13704151031 017720 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
module Hocon::ConfigSyntax
JSON = 0
CONF = 1
# alias 'HOCON' to 'CONF' since some users may be more familiar with that
HOCON = 1
# we're not going to try to support .properties files any time soon :)
#PROPERTIES = 2
end
hocon-1.3.1/lib/hocon/config_render_options.rb 0000644 0000041 0000041 00000002260 13704151031 021425 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
class Hocon::ConfigRenderOptions
def initialize(origin_comments, comments, formatted, json, key_value_separator=:equals)
@origin_comments = origin_comments
@comments = comments
@formatted = formatted
@json = json
@key_value_separator = key_value_separator
end
attr_accessor :origin_comments, :comments, :formatted, :json, :key_value_separator
def origin_comments?
@origin_comments
end
def comments?
@comments
end
def formatted?
@formatted
end
def json?
@json
end
#
# 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
#
def self.defaults
Hocon::ConfigRenderOptions.new(true, true, true, true)
end
#
# 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
#
def self.concise
Hocon::ConfigRenderOptions.new(false, false, false, true)
end
end
hocon-1.3.1/lib/hocon/config_error.rb 0000644 0000041 0000041 00000003325 13704151031 017527 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
class Hocon::ConfigError < StandardError
def initialize(origin, message, cause)
msg =
if origin.nil?
message
else
"#{origin.description}: #{message}"
end
super(msg)
@origin = origin
@cause = cause
end
class ConfigMissingError < Hocon::ConfigError
end
class ConfigNullError < Hocon::ConfigError::ConfigMissingError
def self.make_message(path, expected)
if not expected.nil?
"Configuration key '#{path}' is set to nil but expected #{expected}"
else
"Configuration key '#{path}' is nil"
end
end
end
class ConfigIOError < Hocon::ConfigError
def initialize(origin, message, cause = nil)
super(origin, message, cause)
end
end
class ConfigParseError < Hocon::ConfigError
end
class ConfigWrongTypeError < Hocon::ConfigError
def self.with_expected_actual(origin, path, expected, actual, cause = nil)
ConfigWrongTypeError.new(origin, "#{path} has type #{actual} rather than #{expected}", cause)
end
end
class ConfigBugOrBrokenError < Hocon::ConfigError
def initialize(message, cause = nil)
super(nil, message, cause)
end
end
class ConfigNotResolvedError < Hocon::ConfigError::ConfigBugOrBrokenError
end
class ConfigBadPathError < Hocon::ConfigError
def initialize(origin, path, message, cause = nil)
error_message = !path.nil? ? "Invalid path '#{path}': #{message}" : message
super(origin, error_message, cause)
end
end
class UnresolvedSubstitutionError < ConfigParseError
def initialize(origin, detail, cause = nil)
super(origin, "Could not resolve substitution to a value: " + detail, cause)
end
end
end
hocon-1.3.1/lib/hocon/config_resolve_options.rb 0000644 0000041 0000041 00000001164 13704151031 021627 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
class Hocon::ConfigResolveOptions
attr_reader :use_system_environment, :allow_unresolved
def initialize(use_system_environment, allow_unresolved)
@use_system_environment = use_system_environment
@allow_unresolved = allow_unresolved
end
def set_use_system_environment(value)
self.class.new(value, @allow_unresolved)
end
def set_allow_unresolved(value)
self.class.new(@use_system_environment, value)
end
class << self
def defaults
self.new(true, false)
end
def no_system
defaults.set_use_system_environment(false)
end
end
end
hocon-1.3.1/lib/hocon/config_mergeable.rb 0000644 0000041 0000041 00000005035 13704151031 020321 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_error'
#
# 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.
#
module Hocon::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)
#
def with_fallback(other)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigMergeable` must implement `with_fallback` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/cli.rb 0000644 0000041 0000041 00000016606 13704151031 015626 0 ustar www-data www-data require 'optparse'
require 'hocon'
require 'hocon/version'
require 'hocon/config_render_options'
require 'hocon/config_factory'
require 'hocon/config_value_factory'
require 'hocon/parser/config_document_factory'
require 'hocon/config_error'
module Hocon::CLI
# Aliases
ConfigMissingError = Hocon::ConfigError::ConfigMissingError
ConfigWrongTypeError = Hocon::ConfigError::ConfigWrongTypeError
# List of valid subcommands
SUBCOMMANDS = ['get', 'set', 'unset']
# For when a path can't be found in a hocon config
class MissingPathError < StandardError
end
# Parses the command line flags and argument
# Returns a options hash with values for each option and argument
def self.parse_args(args)
options = {}
opt_parser = OptionParser.new do |opts|
subcommands = SUBCOMMANDS.join(',')
opts.banner = "Usage: hocon [options] {#{subcommands}} PATH [VALUE]\n\n" +
"Example usages:\n" +
" hocon -i settings.conf -o new_settings.conf set some.nested.value 42\n" +
" hocon -f settings.conf set some.nested.value 42\n" +
" cat settings.conf | hocon get some.nested.value\n\n" +
"Subcommands:\n" +
" get PATH - Returns the value at the given path\n" +
" set PATH VALUE - Sets or adds the given value at the given path\n" +
" unset PATH - Removes the value at the given path"
opts.separator('')
opts.separator('Options:')
in_file_description = 'HOCON file to read/modify. If omitted, STDIN assumed'
opts.on('-i', '--in-file HOCON_FILE', in_file_description) do |in_file|
options[:in_file] = in_file
end
out_file_description = 'File to be written to. If omitted, STDOUT assumed'
opts.on('-o', '--out-file HOCON_FILE', out_file_description) do |out_file|
options[:out_file] = out_file
end
file_description = 'File to read/write to. Equivalent to setting -i/-o to the same file'
opts.on('-f', '--file HOCON_FILE', file_description) do |file|
options[:file] = file
end
json_description = "Output values from the 'get' subcommand in json format"
opts.on('-j', '--json', json_description) do |json|
options[:json] = json
end
opts.on_tail('-h', '--help', 'Show this message') do
puts opts
exit
end
opts.on_tail('-v', '--version', 'Show version') do
puts Hocon::Version::STRING
exit
end
end
# parse! returns the argument list minus all the flags it found
remaining_args = opt_parser.parse!(args)
# Ensure -i and -o aren't used at the same time as -f
if (options[:in_file] || options[:out_file]) && options[:file]
exit_with_usage_and_error(opt_parser, "--file can't be used with --in-file or --out-file")
end
# If --file is used, set --in/out-file to the same file
if options[:file]
options[:in_file] = options[:file]
options[:out_file] = options[:file]
end
no_subcommand_error(opt_parser) unless remaining_args.size > 0
# Assume the first arg is the subcommand
subcommand = remaining_args.shift
options[:subcommand] = subcommand
case subcommand
when 'set'
subcommand_arguments_error(subcommand, opt_parser) unless remaining_args.size >= 2
options[:path] = remaining_args.shift
options[:new_value] = remaining_args.shift
when 'get', 'unset'
subcommand_arguments_error(subcommand, opt_parser) unless remaining_args.size >= 1
options[:path] = remaining_args.shift
else
invalid_subcommand_error(subcommand, opt_parser)
end
options
end
# Main entry point into the script
# Calls the appropriate subcommand and handles errors raised from the subcommands
def self.main(opts)
hocon_text = get_hocon_file(opts[:in_file])
begin
case opts[:subcommand]
when 'get'
puts do_get(opts, hocon_text)
when 'set'
print_or_write(do_set(opts, hocon_text), opts[:out_file])
when 'unset'
print_or_write(do_unset(opts, hocon_text), opts[:out_file])
end
rescue MissingPathError
exit_with_error("Can't find the given path: '#{opts[:path]}'")
end
exit
end
# Entry point for the 'get' subcommand
# Returns a string representation of the the value at the path given on the
# command line
def self.do_get(opts, hocon_text)
config = Hocon::ConfigFactory.parse_string(hocon_text)
unless config.has_path?(opts[:path])
raise MissingPathError.new
end
value = config.get_any_ref(opts[:path])
render_options = Hocon::ConfigRenderOptions.defaults
# Otherwise weird comments show up in the output
render_options.origin_comments = false
# If json is false, the hocon format is used
render_options.json = opts[:json]
# Output colons between keys and values
render_options.key_value_separator = :colon
Hocon::ConfigValueFactory.from_any_ref(value).render(render_options)
end
# Entry point for the 'set' subcommand
# Returns a string representation of the HOCON config after adding/replacing
# the value at the given path with the given value
def self.do_set(opts, hocon_text)
config_doc = Hocon::Parser::ConfigDocumentFactory.parse_string(hocon_text)
modified_config_doc = config_doc.set_value(opts[:path], opts[:new_value])
modified_config_doc.render
end
# Entry point for the 'unset' subcommand
# Returns a string representation of the HOCON config after removing the
# value at the given path
def self.do_unset(opts, hocon_text)
config_doc = Hocon::Parser::ConfigDocumentFactory.parse_string(hocon_text)
unless config_doc.has_value?(opts[:path])
raise MissingPathError.new
end
modified_config_doc = config_doc.remove_value(opts[:path])
modified_config_doc.render
end
# If a file is provided, return it's contents. Otherwise read from STDIN
def self.get_hocon_file(in_file)
if in_file
File.read(in_file)
else
STDIN.read
end
end
# Print an error message and exit the program
def self.exit_with_error(message)
STDERR.puts "Error: #{message}"
exit(1)
end
# Print an error message and usage, then exit the program
def self.exit_with_usage_and_error(opt_parser, message)
STDERR.puts opt_parser
exit_with_error(message)
end
# Exits with an error saying there aren't enough arguments found for a given
# subcommand. Prints the usage
def self.subcommand_arguments_error(subcommand, opt_parser)
error_message = "Too few arguments for '#{subcommand}' subcommand"
exit_with_usage_and_error(opt_parser, error_message)
end
# Exits with an error for when no subcommand is supplied on the command line.
# Prints the usage
def self.no_subcommand_error(opt_parser)
error_message = "Must specify subcommand from [#{SUBCOMMANDS.join(', ')}]"
exit_with_usage_and_error(opt_parser, error_message)
end
# Exits with an error for when a subcommand doesn't exist. Prints the usage
def self.invalid_subcommand_error(subcommand, opt_parser)
error_message = "Invalid subcommand '#{subcommand}', must be one of [#{SUBCOMMANDS.join(', ')}]"
exit_with_usage_and_error(opt_parser, error_message)
end
# If out_file is not nil, write to that file. Otherwise print to STDOUT
def self.print_or_write(string, out_file)
if out_file
File.open(out_file, 'w') { |file| file.write(string) }
else
puts string
end
end
end
hocon-1.3.1/lib/hocon/config_object.rb 0000644 0000041 0000041 00000013175 13704151031 017650 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_value'
#
# Subtype of {@link 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 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 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 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 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 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.
#
module Hocon::ConfigObject
include Hocon::ConfigValue
#
# 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
#
def to_config
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `to_config` (#{self.class})"
end
#
# 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
#
def unwrapped
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `unwrapped` (#{self.class})"
end
def with_fallback(other)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `with_fallback` (#{self.class})"
end
#
# Gets a {@link ConfigValue} at the given key, or returns null if there is
# no value. The returned {@link 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
#
def get(key)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `get` (#{self.class})"
end
#
# 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
#
def with_only_key(key)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `with_only_key` (#{self.class})"
end
#
# Clone the object with the given key removed.
#
# @param key
# key to remove
# @return a copy of the object minus the specified key
#
def without_key(key)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `without_key` (#{self.class})"
end
#
# 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
#
def with_value(key, value)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `with_value` (#{self.class})"
end
def with_origin(origin)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigObject should provide their own implementation of `with_origin` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/config_util.rb 0000644 0000041 0000041 00000004315 13704151031 017353 0 ustar www-data www-data require 'hocon/impl/config_impl_util'
# Contains static utility methods
class Hocon::ConfigUtil
#
# Quotes and escapes a string, as in the JSON specification.
#
# @param string
# a string
# @return the string quoted and escaped
#
def self.quote_string(string)
Hocon::Impl::ConfigImplUtil.render_json_string(string)
end
#
# 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
#
def self.join_path(*elements)
Hocon::Impl::ConfigImplUtil.join_path(*elements)
end
#
# 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
#
def self.join_path_from_list(elements)
self.join_path(*elements)
end
#
# 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
#
def self.split_path(path)
Hocon::Impl::ConfigImplUtil.split_path(path)
end
end
hocon-1.3.1/lib/hocon/parser.rb 0000644 0000041 0000041 00000000075 13704151031 016344 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
module Hocon::Parser
end hocon-1.3.1/lib/hocon/config_parse_options.rb 0000644 0000041 0000041 00000003527 13704151031 021267 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
class Hocon::ConfigParseOptions
attr_accessor :syntax, :origin_description, :allow_missing, :includer
def self.defaults
self.new(nil, nil, true, nil)
end
def initialize(syntax, origin_description, allow_missing, includer)
@syntax = syntax
@origin_description = origin_description
@allow_missing = allow_missing
@includer = includer
end
def set_syntax(syntax)
if @syntax == syntax
self
else
Hocon::ConfigParseOptions.new(syntax,
@origin_description,
@allow_missing,
@includer)
end
end
def set_origin_description(origin_description)
if @origin_description == origin_description
self
else
Hocon::ConfigParseOptions.new(@syntax,
origin_description,
@allow_missing,
@includer)
end
end
def set_allow_missing(allow_missing)
if allow_missing? == allow_missing
self
else
Hocon::ConfigParseOptions.new(@syntax,
@origin_description,
allow_missing,
@includer)
end
end
def allow_missing?
@allow_missing
end
def set_includer(includer)
if @includer == includer
self
else
Hocon::ConfigParseOptions.new(@syntax,
@origin_description,
@allow_missing,
includer)
end
end
def append_includer(includer)
if @includer == includer
self
elsif @includer
set_includer(@includer.with_fallback(includer))
else
set_includer(includer)
end
end
end
hocon-1.3.1/lib/hocon/config_list.rb 0000644 0000041 0000041 00000003206 13704151031 017347 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_value'
require 'hocon/config_error'
#
# 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.
#
#
module Hocon::ConfigList
include Hocon::ConfigValue
#
# Recursively unwraps the list, returning a list of plain Java values such
# as Integer or String or whatever is in the list.
#
def unwrapped
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigValue should provide their own implementation of `unwrapped` (#{self.class})"
end
def with_origin(origin)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of ConfigValue should provide their own implementation of `with_origin` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/config_parseable.rb 0000644 0000041 0000041 00000003177 13704151031 020341 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_error'
#
# 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.
#
module Hocon::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
#
def parse(options)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigParseable` must implement `parse` (#{self.class})"
end
#
# Returns a {@link ConfigOrigin} describing the origin of the parseable
# item.
# @return the origin of the parseable item
#
def origin
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigParseable` must implement `origin` (#{self.class})"
end
#
# 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
#
def options
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigParseable` must implement `options` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/config_includer_file.rb 0000644 0000041 0000041 00000001717 13704151031 021205 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_error'
#
# 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.
#
module Hocon::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
#
def include_file(context, what)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigIncluderFile` must implement `include_file` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/config.rb 0000644 0000041 0000041 00000114735 13704151031 016326 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_mergeable'
require 'hocon/config_error'
#
# 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 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(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 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 ConfigUtil#joinPath} and {@link 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 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.
#
#
# 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 ConfigValue#valueType() valueType()} of {@link ConfigValueType#NULL
# NULL}. Instead, they throw {@link 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 ConfigException.Null} will be
# thrown. {@link 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 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(ConfigMergeable)}. If
# you use {@link 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 ConfigObject#render()} on the root object,
# myConfig.root().render(). There's also a variant
# {@link ConfigObject#render(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 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.
#
class Config < Hocon::ConfigMergeable
#
# Gets the {@code Config} as a tree of {@link 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
#
def root
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `root` (#{self.class})"
end
#
# 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
#
def origin
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `origin` (#{self.class})"
end
def with_fallback(other)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `with_fallback` (#{self.class})"
end
#
# 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 ConfigResolveOptions#defaults()}, there is
# another variant {@link Config#resolve(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:
#
#
# Many methods on {@link 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 ConfigException.UnresolvedSubstitution
# if any substitutions refer to nonexistent paths
# @throws ConfigException
# some other config exception if there are other problems
#
def resolve(options)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `resolve` (#{self.class})"
end
#
# 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(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
#
def resolved?
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `resolved?` (#{self.class})"
end
#
# 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
#
def resolve_with(source, options)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `resolve_with` (#{self.class})"
end
#
# 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:
#
#
#
All paths found in the reference config must be present in this
# config or an exception will be thrown.
#
# Some changes in type from the reference config to this config will cause
# an exception to be thrown. Not all potential type problems are detected,
# in particular it's assumed that strings are compatible with everything
# except objects and lists. This is because string types are often "really"
# some other type (system properties always start out as strings, or a
# string like "5ms" could be used with {@link #getMilliseconds}). Also,
# it's allowed to set any type to null or override null with any type.
#
# Any unresolved substitutions in this config will cause a validation
# failure; both the reference config and this config should be resolved
# before validation. If the reference config is unresolved, it's a bug in
# the caller of this method.
#
#
#
# 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 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 ConfigException.ValidationFailed
# if there are any validation issues
# @throws ConfigException.NotResolved
# if this config is not resolved
# @throws ConfigException.BugOrBroken
# if the reference config is unresolved or caller otherwise
# misuses the API
#
def check_valid(reference, restrict_to_paths)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `check_valid` (#{self.class})"
end
#
# 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 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 ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
#
# @param path
# the path expression
# @return true if a non-null value is present at the path
# @throws ConfigException.BadPath
# if the path expression is invalid
#
def has_path(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `has_path` (#{self.class})"
end
#
# Returns true if the {@code Config}'s root object contains no key-value
# pairs.
#
# @return true if the configuration is empty
#
def empty?
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `empty` (#{self.class})"
end
#
# 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 ConfigUtil#splitPath}.
#
# Because a Config is conceptually a single-level map from
# paths to values, there will not be any {@link ConfigObject} values in the
# entries (that is, all entries represent leaf nodes). Use
# {@link ConfigObject} rather than Config if you want a tree.
# (OK, this is a slight lie: Config entries may contain
# {@link 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 ConfigObject} and creating an entry for
# each leaf value.
#
def entry_set
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `entry_set` (#{self.class})"
end
#
#
# @param path
# path expression
# @return the boolean value at the requested path
# @throws ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to boolean
#
def get_boolean(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_boolean` (#{self.class})"
end
#
# @param path
# path expression
# @return the numeric value at the requested path
# @throws ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a number
#
def get_number(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_number` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to an int (for example it is out
# of range, or it's a boolean value)
#
def get_int(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_int` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a long
#
def get_long(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_long` (#{self.class})"
end
#
# @param path
# path expression
# @return the floating-point value at the requested path
# @throws ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a double
#
def get_double(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_double` (#{self.class})"
end
#
# @param path
# path expression
# @return the string value at the requested path
# @throws ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a string
#
def get_string(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_string` (#{self.class})"
end
#
# @param path
# path expression
# @return the {@link ConfigObject} value at the requested path
# @throws ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to an object
#
def get_object(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_object` (#{self.class})"
end
#
# @param path
# path expression
# @return the nested {@code Config} value at the requested path
# @throws ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a Config
#
def get_config(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_config` (#{self.class})"
end
#
# 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 ConfigValue#unwrapped()}).
#
# @param path
# path expression
# @return the unwrapped value at the requested path
# @throws ConfigException.Missing
# if value is absent or null
#
def get_any_ref(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_any_ref` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
#
def get_value(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_value`path(#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to Long or String
# @throws ConfigException.BadValue
# if value cannot be parsed as a size in bytes
#
def get_bytes(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_bytes` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to Long or String
# @throws ConfigException.BadValue
# if value cannot be parsed as a size in bytes
#
def get_memory_size(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_memory_size`path(#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to Long or String
# @throws ConfigException.BadValue
# if value cannot be parsed as a number of milliseconds
#
def get_milliseconds(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_milliseconds` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to Long or String
# @throws ConfigException.BadValue
# if value cannot be parsed as a number of nanoseconds
#
def get_nanoseconds(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_nanoseconds` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to Long or String
# @throws ConfigException.BadValue
# if value cannot be parsed as a number of the given TimeUnit
#
def get_duration(path, unit)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_duration` (#{self.class})"
end
#
# Gets a list value (with any element type) as a {@link 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 ConfigList} at the path
# @throws ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a ConfigList
#
def get_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_list` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list of booleans
#
def get_boolean_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_boolean_list` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list of numbers
#
def get_number_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_number_list` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list of ints
#
def get_int_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_int_list` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list of longs
#
def get_long_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_long_list` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list of doubles
#
def get_double_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_double_list` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list of strings
#
def get_string_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_string_list` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list of objects
#
def get_object_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_object_list` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list of configs
#
def get_config_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_config_list` (#{self.class})"
end
#
# 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 ConfigValue#unwrapped()}).
#
# @param path
# the path to the list value.
# @return the list at the path
# @throws ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list
#
def get_any_ref_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_any_ref_list`path(#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list of memory sizes
#
def get_bytes_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_bytes_list` (#{self.class})"
end
#
# 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 ConfigException.Missing
# if value is absent or null
# @throws ConfigException.WrongType
# if value is not convertible to a list of memory sizes
#
def get_memory_size_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_memory_size_list` (#{self.class})"
end
#
# @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)}
# @param path the path
# @return list of millisecond values
#
def get_milliseconds_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_milliseconds_list` (#{self.class})"
end
#
# @deprecated As of release 1.1, replaced by {@link #getDurationList(String, TimeUnit)}
# @param path the path
# @return list of nanosecond values
#
def get_nanoseconds_list(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_nanoseconds_list` (#{self.class})"
end
#
# 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
#
def get_duration_list(path, unit)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `get_duration_list` (#{self.class})"
end
#
# 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 ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
#
# @param path
# path to keep
# @return a copy of the config minus all paths except the one specified
#
def with_only_path(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `with_only_path` (#{self.class})"
end
#
# Clone the config with the given path removed.
#
# Note that path expressions have a syntax and sometimes require quoting
# (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
#
# @param path
# path expression to remove
# @return a copy of the config minus the specified path
#
def without_path(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `without_path` (#{self.class})"
end
#
# Places the config inside another {@code Config} at the given path.
#
# Note that path expressions have a syntax and sometimes require quoting
# (see {@link ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
#
# @param path
# path expression to store this config at.
# @return a {@code Config} instance containing this config at the given
# path.
#
def at_path(path)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `at_path` (#{self.class})"
end
#
# 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 ConfigUtil#joinPath} and {@link ConfigUtil#splitPath}).
#
# @param key
# key to store this config at.
# @return a {@code Config} instance containing this config at the given
# key.
#
def at_key(key)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `at_key` (#{self.class})"
end
#
# 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 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
#
def with_value(path, value)
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Config` must implement `with_value` (#{self.class})"
end
end
hocon-1.3.1/lib/hocon/config_value_factory.rb 0000644 0000041 0000041 00000006000 13704151031 021232 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/impl/config_impl'
class Hocon::ConfigValueFactory
ConfigImpl = Hocon::Impl::ConfigImpl
#
# Creates a {@link ConfigValue} from a plain value, which may be
# a Boolean, Number, String,
# Hash, or nil. A
# Hash must be a Hash from String to more values
# that can be supplied to from_any_ref(). A Hash
# will become a {@link ConfigObject} and an Array will become a
# {@link ConfigList}.
#
#
# In a Hash passed to from_any_ref(), the map's keys
# are plain keys, not path expressions. So if your Hash 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 origin_description 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 origin_description 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
# origin_description 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 origin_description
# name of origin file or brief description of what the value is
# @return a new value
#
def self.from_any_ref(object, origin_description = nil)
if object.is_a?(Hash)
from_map(object, origin_description)
else
ConfigImpl.from_any_ref(object, origin_description)
end
end
#
# See the {@link #from_any_ref(Object,String)} documentation for details
#
#
# See also {@link ConfigFactory#parse_map(Map)} which interprets the keys in
# the map as path expressions.
#
# @param values map from keys to plain ruby values
# @return a new {@link ConfigObject}
#
def self.from_map(values, origin_description = nil)
ConfigImpl.from_any_ref(process_hash(values), origin_description)
end
private
def self.process_hash(hash)
Hash[hash.map {|k, v| [k.is_a?(Symbol) ? k.to_s : k, v.is_a?(Hash) ? process_hash(v) : v]}]
end
end
hocon-1.3.1/lib/hocon/config_value.rb 0000644 0000041 0000041 00000010177 13704151031 017515 0 ustar www-data www-data # encoding: utf-8
require 'hocon'
require 'hocon/config_mergeable'
#
# 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.
#
module Hocon::ConfigValue
include Hocon::ConfigMergeable
#
# The origin of the value (file, line number, etc.), for debugging and
# error messages.
#
# @return where the value came from
#
def origin
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigValue` must implement `origin` (#{self.class})"
end
#
# The {@link ConfigValueType} of the value; matches the JSON type schema.
#
# @return value's type
#
def value_type
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ConfigValue` must implement `value_type` (#{self.class})"
end
#
# Returns the value as a plain Java boxed value, that is, a {@code String},
# {@code Number}, {@code Boolean}, {@code Map},
# {@code List