001package net.minecraftforge.common;
002
003import static net.minecraftforge.common.Configuration.NEW_LINE;
004import static net.minecraftforge.common.Configuration.allowedProperties;
005
006import java.io.BufferedWriter;
007import java.io.IOException;
008import java.util.ArrayList;
009import java.util.Collection;
010import java.util.Map;
011import java.util.Set;
012import java.util.TreeMap;
013import com.google.common.collect.ImmutableMap;
014import com.google.common.collect.ImmutableSet;
015import com.google.common.base.Splitter;
016
017public class ConfigCategory implements Map<String, Property>
018{
019    private String name;
020    private String comment;
021    private ArrayList<ConfigCategory> children = new ArrayList<ConfigCategory>();
022    private Map<String, Property> properties = new TreeMap<String, Property>();
023    public final ConfigCategory parent;
024    private boolean changed = false;
025
026    public ConfigCategory(String name)
027    {
028        this(name, null);
029    }
030
031    public ConfigCategory(String name, ConfigCategory parent)
032    {
033        this.name = name;
034        this.parent = parent;
035        if (parent != null)
036        {
037            parent.children.add(this);
038        }
039    }
040
041    public boolean equals(Object obj)
042    {
043        if (obj instanceof ConfigCategory)
044        {
045            ConfigCategory cat = (ConfigCategory)obj;
046            return name.equals(cat.name) && children.equals(cat.children);  
047        }
048        
049        return false;
050    }
051
052    public String getQualifiedName()
053    {
054        return getQualifiedName(name, parent);
055    }
056
057    public static String getQualifiedName(String name, ConfigCategory parent)
058    {
059        return (parent == null ? name : parent.getQualifiedName() + Configuration.CATEGORY_SPLITTER + name);
060    }
061
062    public ConfigCategory getFirstParent()
063    {
064        return (parent == null ? this : parent.getFirstParent());
065    }
066
067    public boolean isChild()
068    {
069        return parent != null;
070    }
071
072    public Map<String, Property> getValues()
073    {
074        return ImmutableMap.copyOf(properties);
075    }
076
077    public void setComment(String comment)
078    {
079        this.comment = comment;
080    }
081
082    public boolean containsKey(String key)
083    {
084        return properties.containsKey(key);
085    }
086
087    public Property get(String key)
088    {
089        return properties.get(key);
090    }
091
092    private void write(BufferedWriter out, String... data) throws IOException
093    {
094        write(out, true, data);
095    }
096
097    private void write(BufferedWriter out, boolean new_line, String... data) throws IOException
098    {
099        for (int x = 0; x < data.length; x++)
100        {
101            out.write(data[x]);
102        }
103        if (new_line) out.write(NEW_LINE);
104    }
105
106    public void write(BufferedWriter out, int indent) throws IOException
107    {
108        String pad0 = getIndent(indent);
109        String pad1 = getIndent(indent + 1);
110        String pad2 = getIndent(indent + 2);
111
112        write(out, pad0, "####################");
113        write(out, pad0, "# ", name);
114
115        if (comment != null)
116        {
117            write(out, pad0, "#===================");
118            Splitter splitter = Splitter.onPattern("\r?\n");
119
120            for (String line : splitter.split(comment))
121            {
122                write(out, pad0, "# ", line);
123            }
124        }
125
126        write(out, pad0, "####################", NEW_LINE);
127
128        if (!allowedProperties.matchesAllOf(name))
129        {
130            name = '"' + name + '"';
131        }
132
133        write(out, pad0, name, " {");
134
135        Property[] props = properties.values().toArray(new Property[properties.size()]);
136
137        for (int x = 0; x < props.length; x++)
138        {
139            Property prop = props[x];
140
141            if (prop.comment != null)
142            {
143                if (x != 0)
144                {
145                    out.newLine();
146                }
147
148                Splitter splitter = Splitter.onPattern("\r?\n");
149                for (String commentLine : splitter.split(prop.comment))
150                {
151                    write(out, pad1, "# ", commentLine);
152                }
153            }
154
155            String propName = prop.getName();
156
157            if (!allowedProperties.matchesAllOf(propName))
158            {
159                propName = '"' + propName + '"';
160            }
161
162            if (prop.isList())
163            {
164                char type = prop.getType().getID();
165                
166                write(out, pad1, String.valueOf(type), ":", propName, " <");
167
168                for (String line : prop.getStringList())
169                {
170                    write(out, pad2, line);
171                }
172
173                write(out, pad1, " >");
174            }
175            else if (prop.getType() == null)
176            {
177                write(out, pad1, propName, "=", prop.getString());
178            }
179            else
180            {
181                char type = prop.getType().getID();
182                write(out, pad1, String.valueOf(type), ":", propName, "=", prop.getString());
183            }
184        }
185
186        for (ConfigCategory child : children)
187        {
188            child.write(out, indent + 1);
189        }
190
191        write(out, pad0, "}", NEW_LINE);
192    }
193
194    private String getIndent(int indent)
195    {
196        StringBuilder buf = new StringBuilder("");
197        for (int x = 0; x < indent; x++)
198        {
199            buf.append("    ");
200        }
201        return buf.toString();
202    }
203
204    public boolean hasChanged()
205    {
206        if (changed) return true;
207        for (Property prop : properties.values())
208        {
209            if (prop.hasChanged()) return true;
210        }
211        return false;
212    }
213
214    void resetChangedState()
215    {
216        changed = false;
217        for (Property prop : properties.values())
218        {
219            prop.resetChangedState();
220        }
221    }
222
223
224    //Map bouncer functions for compatibility with older mods, to be removed once all mods stop using it.
225    @Override public int size(){ return properties.size(); }
226    @Override public boolean isEmpty() { return properties.isEmpty(); }
227    @Override public boolean containsKey(Object key) { return properties.containsKey(key); }
228    @Override public boolean containsValue(Object value){ return properties.containsValue(value); }
229    @Override public Property get(Object key) { return properties.get(key); }
230    @Override public Property put(String key, Property value)
231    {
232        changed = true;
233        return properties.put(key, value);
234    }
235    @Override public Property remove(Object key)
236    {
237        changed = true;
238        return properties.remove(key);
239    }
240    @Override public void putAll(Map<? extends String, ? extends Property> m)
241    {
242        changed = true;
243        properties.putAll(m);
244    }
245    @Override public void clear()
246    {
247        changed = true;
248        properties.clear();
249    }
250    @Override public Set<String> keySet() { return properties.keySet(); }
251    @Override public Collection<Property> values() { return properties.values(); }
252
253    @Override //Immutable copy, changes will NOT be reflected in this category
254    public Set<java.util.Map.Entry<String, Property>> entrySet()
255    {
256        return ImmutableSet.copyOf(properties.entrySet());
257    }
258
259}