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