001 /** 002 * This software is provided under the terms of the Minecraft Forge Public 003 * License v1.0. 004 */ 005 006 package net.minecraftforge.common; 007 008 import java.io.BufferedReader; 009 import java.io.BufferedWriter; 010 import java.io.File; 011 import java.io.FileInputStream; 012 import java.io.FileOutputStream; 013 import java.io.IOException; 014 import java.io.InputStreamReader; 015 import java.io.OutputStreamWriter; 016 import java.text.DateFormat; 017 import java.util.Collection; 018 import java.util.Date; 019 import java.util.Locale; 020 import java.util.Map; 021 import java.util.TreeMap; 022 023 import net.minecraft.src.Block; 024 025 /** 026 * This class offers advanced configurations capabilities, allowing to provide 027 * various categories for configuration variables. 028 */ 029 public class Configuration 030 { 031 032 private boolean configBlocks[] = null; 033 034 public static final String CATEGORY_GENERAL = "general"; 035 public static final String CATEGORY_BLOCK = "block"; 036 public static final String CATEGORY_ITEM = "item"; 037 038 File file; 039 040 public Map<String, Map<String, Property>> categories = new TreeMap<String, Map<String, Property>>(); 041 042 public TreeMap<String, Property> blockProperties = new TreeMap<String, Property>(); 043 public TreeMap<String, Property> itemProperties = new TreeMap<String, Property>(); 044 public TreeMap<String, Property> generalProperties = new TreeMap<String, Property>(); 045 public static final String ALLOWED_CHARS = "._-"; 046 047 /** 048 * Create a configuration file for the file given in parameter. 049 */ 050 public Configuration(File file) 051 { 052 this.file = file; 053 categories.put(CATEGORY_GENERAL, generalProperties); 054 categories.put(CATEGORY_BLOCK, blockProperties); 055 categories.put(CATEGORY_ITEM, itemProperties); 056 } 057 058 /** 059 * Gets or create a block id property. If the block id property key is 060 * already in the configuration, then it will be used. Otherwise, 061 * defaultId will be used, except if already taken, in which case this 062 * will try to determine a free default id. 063 */ 064 public Property getOrCreateBlockIdProperty(String key, int defaultId) 065 { 066 if (configBlocks == null) 067 { 068 configBlocks = new boolean[Block.blocksList.length]; 069 070 for (int i = 0; i < configBlocks.length; ++i) 071 { 072 configBlocks[i] = false; 073 } 074 } 075 076 Map<String, Property> properties = categories.get(CATEGORY_BLOCK); 077 if (properties.containsKey(key)) 078 { 079 Property property = getOrCreateIntProperty(key, Configuration.CATEGORY_BLOCK, defaultId); 080 configBlocks[Integer.parseInt(property.value)] = true; 081 return property; 082 } 083 else 084 { 085 Property property = new Property(); 086 properties.put(key, property); 087 property.setName(key); 088 089 if (Block.blocksList[defaultId] == null && !configBlocks[defaultId]) 090 { 091 property.value = Integer.toString(defaultId); 092 configBlocks[defaultId] = true; 093 return property; 094 } 095 else 096 { 097 for (int j = configBlocks.length - 1; j >= 0; --j) 098 { 099 if (Block.blocksList[j] == null && !configBlocks[j]) 100 { 101 property.value = Integer.toString(j); 102 configBlocks[j] = true; 103 return property; 104 } 105 } 106 107 throw new RuntimeException("No more block ids available for " + key); 108 } 109 } 110 } 111 112 public Property getOrCreateIntProperty(String key, String category, int defaultValue) 113 { 114 Property prop = getOrCreateProperty(key, category, Integer.toString(defaultValue)); 115 try 116 { 117 Integer.parseInt(prop.value); 118 return prop; 119 } 120 catch (NumberFormatException e) 121 { 122 prop.value = Integer.toString(defaultValue); 123 return prop; 124 } 125 } 126 127 public Property getOrCreateBooleanProperty(String key, String category, boolean defaultValue) 128 { 129 Property prop = getOrCreateProperty(key, category, Boolean.toString(defaultValue)); 130 if ("true".equals(prop.value.toLowerCase(Locale.ENGLISH)) || "false".equals(prop.value.toLowerCase(Locale.ENGLISH))) 131 { 132 return prop; 133 } 134 else 135 { 136 prop.value = Boolean.toString(defaultValue); 137 return prop; 138 } 139 } 140 141 public Property getOrCreateProperty(String key, String category, String defaultValue) 142 { 143 category = category.toLowerCase(Locale.ENGLISH); 144 Map<String, Property> source = categories.get(category); 145 146 if(source == null) 147 { 148 source = new TreeMap<String, Property>(); 149 categories.put(category, source); 150 } 151 152 if (source.containsKey(key)) 153 { 154 return source.get(key); 155 } 156 else if (defaultValue != null) 157 { 158 Property property = new Property(); 159 160 source.put(key, property); 161 property.setName(key); 162 163 property.value = defaultValue; 164 return property; 165 } 166 else 167 { 168 return null; 169 } 170 } 171 172 public void load() 173 { 174 BufferedReader buffer = null; 175 try 176 { 177 if (file.getParentFile() != null) 178 { 179 file.getParentFile().mkdirs(); 180 } 181 182 if (!file.exists() && !file.createNewFile()) 183 { 184 return; 185 } 186 187 if (file.canRead()) 188 { 189 FileInputStream fileinputstream = new FileInputStream(file); 190 buffer = new BufferedReader(new InputStreamReader(fileinputstream, "UTF-8")); 191 192 String line; 193 Map<String, Property> currentMap = null; 194 195 while (true) 196 { 197 line = buffer.readLine(); 198 199 if (line == null) 200 { 201 break; 202 } 203 204 int nameStart = -1, nameEnd = -1; 205 boolean skip = false; 206 207 for (int i = 0; i < line.length() && !skip; ++i) 208 { 209 if (Character.isLetterOrDigit(line.charAt(i)) || ALLOWED_CHARS.indexOf(line.charAt(i)) != -1) 210 { 211 if (nameStart == -1) 212 { 213 nameStart = i; 214 } 215 216 nameEnd = i; 217 } 218 else if (Character.isWhitespace(line.charAt(i))) 219 { 220 // ignore space charaters 221 } 222 else 223 { 224 switch (line.charAt(i)) 225 { 226 case '#': 227 skip = true; 228 continue; 229 230 case '{': 231 String scopeName = line.substring(nameStart, nameEnd + 1); 232 233 currentMap = categories.get(scopeName); 234 if (currentMap == null) 235 { 236 currentMap = new TreeMap<String, Property>(); 237 categories.put(scopeName, currentMap); 238 } 239 240 break; 241 242 case '}': 243 currentMap = null; 244 break; 245 246 case '=': 247 String propertyName = line.substring(nameStart, nameEnd + 1); 248 249 if (currentMap == null) 250 { 251 throw new RuntimeException("property " + propertyName + " has no scope"); 252 } 253 254 Property prop = new Property(); 255 prop.setName(propertyName); 256 prop.value = line.substring(i + 1); 257 i = line.length(); 258 259 currentMap.put(propertyName, prop); 260 261 break; 262 263 default: 264 throw new RuntimeException("unknown character " + line.charAt(i)); 265 } 266 } 267 } 268 } 269 } 270 } 271 catch (IOException e) 272 { 273 e.printStackTrace(); 274 } 275 finally 276 { 277 if (buffer != null) 278 { 279 try 280 { 281 buffer.close(); 282 } catch (IOException e){} 283 } 284 } 285 } 286 287 public void save() 288 { 289 try 290 { 291 if (file.getParentFile() != null) 292 { 293 file.getParentFile().mkdirs(); 294 } 295 296 if (!file.exists() && !file.createNewFile()) 297 { 298 return; 299 } 300 301 if (file.canWrite()) 302 { 303 FileOutputStream fos = new FileOutputStream(file); 304 BufferedWriter buffer = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8")); 305 306 buffer.write("# Configuration file\r\n"); 307 buffer.write("# Generated on " + DateFormat.getInstance().format(new Date()) + "\r\n"); 308 buffer.write("\r\n"); 309 310 for(Map.Entry<String, Map<String, Property>> category : categories.entrySet()) 311 { 312 buffer.write("####################\r\n"); 313 buffer.write("# " + category.getKey() + " \r\n"); 314 buffer.write("####################\r\n\r\n"); 315 316 buffer.write(category.getKey() + " {\r\n"); 317 writeProperties(buffer, category.getValue().values()); 318 buffer.write("}\r\n\r\n"); 319 } 320 321 buffer.close(); 322 fos.close(); 323 } 324 } 325 catch (IOException e) 326 { 327 e.printStackTrace(); 328 } 329 } 330 331 private void writeProperties(BufferedWriter buffer, Collection<Property> props) throws IOException 332 { 333 for (Property property : props) 334 { 335 if (property.comment != null) 336 { 337 buffer.write(" # " + property.comment + "\r\n"); 338 } 339 340 buffer.write(" " + property.getName() + "=" + property.value); 341 buffer.write("\r\n"); 342 } 343 } 344 }