001/** 002 * Copyright (C) 2011-2012 Typesafe Inc. <http://typesafe.com> 003 */ 004package com.typesafe.config; 005 006 007import com.typesafe.config.impl.ConfigImplUtil; 008 009/** 010 * A set of options related to parsing. 011 * 012 * <p> 013 * This object is immutable, so the "setters" return a new object. 014 * 015 * <p> 016 * Here is an example of creating a custom {@code ConfigParseOptions}: 017 * 018 * <pre> 019 * ConfigParseOptions options = ConfigParseOptions.defaults() 020 * .setSyntax(ConfigSyntax.JSON) 021 * .setAllowMissing(false) 022 * </pre> 023 * 024 */ 025public final class ConfigParseOptions { 026 final ConfigSyntax syntax; 027 final String originDescription; 028 final boolean allowMissing; 029 final ConfigIncluder includer; 030 final ClassLoader classLoader; 031 032 private ConfigParseOptions(ConfigSyntax syntax, String originDescription, boolean allowMissing, 033 ConfigIncluder includer, ClassLoader classLoader) { 034 this.syntax = syntax; 035 this.originDescription = originDescription; 036 this.allowMissing = allowMissing; 037 this.includer = includer; 038 this.classLoader = classLoader; 039 } 040 041 /** 042 * Gets an instance of <code>ConfigParseOptions</code> with all fields 043 * set to the default values. Start with this instance and make any 044 * changes you need. 045 * @return the default parse options 046 */ 047 public static ConfigParseOptions defaults() { 048 return new ConfigParseOptions(null, null, true, null, null); 049 } 050 051 /** 052 * Set the file format. If set to null, try to guess from any available 053 * filename extension; if guessing fails, assume {@link ConfigSyntax#CONF}. 054 * 055 * @param syntax 056 * a syntax or {@code null} for best guess 057 * @return options with the syntax set 058 */ 059 public ConfigParseOptions setSyntax(ConfigSyntax syntax) { 060 if (this.syntax == syntax) 061 return this; 062 else 063 return new ConfigParseOptions(syntax, this.originDescription, this.allowMissing, 064 this.includer, this.classLoader); 065 } 066 067 /** 068 * Set the file format. If set to null, assume {@link ConfigSyntax#CONF}. 069 * 070 * @param filename 071 * a configuration file name 072 * @return options with the syntax set 073 */ 074 public ConfigParseOptions setSyntaxFromFilename(String filename) { 075 ConfigSyntax syntax = ConfigImplUtil.syntaxFromExtension(filename); 076 return setSyntax(syntax); 077 } 078 079 /** 080 * Gets the current syntax option, which may be null for "any". 081 * @return the current syntax or null 082 */ 083 public ConfigSyntax getSyntax() { 084 return syntax; 085 } 086 087 /** 088 * Set a description for the thing being parsed. In most cases this will be 089 * set up for you to something like the filename, but if you provide just an 090 * input stream you might want to improve on it. Set to null to allow the 091 * library to come up with something automatically. This description is the 092 * basis for the {@link ConfigOrigin} of the parsed values. 093 * 094 * @param originDescription description to put in the {@link ConfigOrigin} 095 * @return options with the origin description set 096 */ 097 public ConfigParseOptions setOriginDescription(String originDescription) { 098 // findbugs complains about == here but is wrong, do not "fix" 099 if (this.originDescription == originDescription) 100 return this; 101 else if (this.originDescription != null && originDescription != null 102 && this.originDescription.equals(originDescription)) 103 return this; 104 else 105 return new ConfigParseOptions(this.syntax, originDescription, this.allowMissing, 106 this.includer, this.classLoader); 107 } 108 109 /** 110 * Gets the current origin description, which may be null for "automatic". 111 * @return the current origin description or null 112 */ 113 public String getOriginDescription() { 114 return originDescription; 115 } 116 117 /** this is package-private, not public API */ 118 ConfigParseOptions withFallbackOriginDescription(String originDescription) { 119 if (this.originDescription == null) 120 return setOriginDescription(originDescription); 121 else 122 return this; 123 } 124 125 /** 126 * Set to false to throw an exception if the item being parsed (for example 127 * a file) is missing. Set to true to just return an empty document in that 128 * case. Note that this setting applies on only to fetching the root document, 129 * it has no effect on any nested includes. 130 * 131 * @param allowMissing true to silently ignore missing item 132 * @return options with the "allow missing" flag set 133 */ 134 public ConfigParseOptions setAllowMissing(boolean allowMissing) { 135 if (this.allowMissing == allowMissing) 136 return this; 137 else 138 return new ConfigParseOptions(this.syntax, this.originDescription, allowMissing, 139 this.includer, this.classLoader); 140 } 141 142 /** 143 * Gets the current "allow missing" flag. 144 * @return whether we allow missing files 145 */ 146 public boolean getAllowMissing() { 147 return allowMissing; 148 } 149 150 /** 151 * Set a {@link ConfigIncluder} which customizes how includes are handled. 152 * null means to use the default includer. 153 * 154 * @param includer the includer to use or null for default 155 * @return new version of the parse options with different includer 156 */ 157 public ConfigParseOptions setIncluder(ConfigIncluder includer) { 158 if (this.includer == includer) 159 return this; 160 else 161 return new ConfigParseOptions(this.syntax, this.originDescription, this.allowMissing, 162 includer, this.classLoader); 163 } 164 165 /** 166 * Prepends a {@link ConfigIncluder} which customizes how 167 * includes are handled. To prepend your includer, the 168 * library calls {@link ConfigIncluder#withFallback} on your 169 * includer to append the existing includer to it. 170 * 171 * @param includer the includer to prepend (may not be null) 172 * @return new version of the parse options with different includer 173 */ 174 public ConfigParseOptions prependIncluder(ConfigIncluder includer) { 175 if (includer == null) 176 throw new NullPointerException("null includer passed to prependIncluder"); 177 if (this.includer == includer) 178 return this; 179 else if (this.includer != null) 180 return setIncluder(includer.withFallback(this.includer)); 181 else 182 return setIncluder(includer); 183 } 184 185 /** 186 * Appends a {@link ConfigIncluder} which customizes how 187 * includes are handled. To append, the library calls {@link 188 * ConfigIncluder#withFallback} on the existing includer. 189 * 190 * @param includer the includer to append (may not be null) 191 * @return new version of the parse options with different includer 192 */ 193 public ConfigParseOptions appendIncluder(ConfigIncluder includer) { 194 if (includer == null) 195 throw new NullPointerException("null includer passed to appendIncluder"); 196 if (this.includer == includer) 197 return this; 198 else if (this.includer != null) 199 return setIncluder(this.includer.withFallback(includer)); 200 else 201 return setIncluder(includer); 202 } 203 204 /** 205 * Gets the current includer (will be null for the default includer). 206 * @return current includer or null 207 */ 208 public ConfigIncluder getIncluder() { 209 return includer; 210 } 211 212 /** 213 * Set the class loader. If set to null, 214 * <code>Thread.currentThread().getContextClassLoader()</code> will be used. 215 * 216 * @param loader 217 * a class loader or {@code null} to use thread context class 218 * loader 219 * @return options with the class loader set 220 */ 221 public ConfigParseOptions setClassLoader(ClassLoader loader) { 222 if (this.classLoader == loader) 223 return this; 224 else 225 return new ConfigParseOptions(this.syntax, this.originDescription, this.allowMissing, 226 this.includer, loader); 227 } 228 229 /** 230 * Get the class loader; never returns {@code null}, if the class loader was 231 * unset, returns 232 * <code>Thread.currentThread().getContextClassLoader()</code>. 233 * 234 * @return class loader to use 235 */ 236 public ClassLoader getClassLoader() { 237 if (this.classLoader == null) 238 return Thread.currentThread().getContextClassLoader(); 239 else 240 return this.classLoader; 241 } 242}