001/*
002 * Configurate
003 * Copyright (C) zml and Configurate contributors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package ninja.leaping.configurate.attributed;
018
019import com.google.common.base.Strings;
020import com.google.common.collect.ImmutableMap;
021import ninja.leaping.configurate.ConfigurationNode;
022import ninja.leaping.configurate.ConfigurationOptions;
023import ninja.leaping.configurate.SimpleConfigurationNode;
024import ninja.leaping.configurate.commented.SimpleCommentedConfigurationNode;
025
026import org.checkerframework.checker.nullness.qual.NonNull;
027import org.checkerframework.checker.nullness.qual.Nullable;
028
029import java.util.List;
030import java.util.LinkedHashMap;
031import java.util.Map;
032
033/**
034 * Basic implementation of {@link AttributedConfigurationNode}.
035 */
036public class SimpleAttributedConfigurationNode extends SimpleCommentedConfigurationNode implements AttributedConfigurationNode {
037    private String tagName;
038    private final Map<String, String> attributes = new LinkedHashMap<>();
039
040    /**
041     * Create a new node with no parent.
042     *
043     * @return a new node
044     * @deprecated Use {@link AttributedConfigurationNode#root()} instead
045     */
046    @Deprecated
047    @NonNull
048    public static SimpleAttributedConfigurationNode root() {
049        return root("root", ConfigurationOptions.defaults());
050    }
051
052
053    /**
054     * Create a new node with no parent.
055     *
056     * @param tagName The name of the tag to be used to represent this node
057     * @return a new node
058     * @deprecated Use {@link AttributedConfigurationNode#root(String)} instead
059     */
060    @Deprecated
061    @NonNull
062    public static SimpleAttributedConfigurationNode root(@NonNull String tagName) {
063        return root(tagName, ConfigurationOptions.defaults());
064    }
065
066
067    /**
068     * Create a new node with no parent, a specified tag name, and specific options.
069     *
070     * @param tagName The name of the tag to be used to represent this node
071     * @param options The options to use within this node
072     * @return a new node
073     * @deprecated Use {@link AttributedConfigurationNode#root(String, ConfigurationOptions)} instead
074     */
075    @Deprecated
076    @NonNull
077    public static SimpleAttributedConfigurationNode root(@NonNull String tagName, @NonNull ConfigurationOptions options) {
078        return new SimpleAttributedConfigurationNode(tagName, null, null, options);
079    }
080
081    protected SimpleAttributedConfigurationNode(@NonNull String tagName, @Nullable Object path, @Nullable SimpleConfigurationNode parent, @NonNull ConfigurationOptions options) {
082        super(path, parent, options);
083        setTagName(tagName);
084    }
085
086    protected SimpleAttributedConfigurationNode(@NonNull String tagName, @Nullable SimpleConfigurationNode parent, @NonNull SimpleConfigurationNode copyOf) {
087        super(parent, copyOf);
088        setTagName(tagName);
089    }
090
091    @NonNull
092    @Override
093    public String getTagName() {
094        return tagName;
095    }
096
097    @NonNull
098    @Override
099    public SimpleAttributedConfigurationNode setTagName(@NonNull String tagName) {
100        if (Strings.isNullOrEmpty(tagName)) {
101            throw new IllegalArgumentException("Tag name cannot be null/empty");
102        }
103
104        this.tagName = tagName;
105        return this;
106    }
107
108    @NonNull
109    @Override
110    public SimpleAttributedConfigurationNode addAttribute(@NonNull String name, @NonNull String value) {
111        if (Strings.isNullOrEmpty(name)) {
112            throw new IllegalArgumentException("Attribute name cannot be null/empty");
113        }
114        attachIfNecessary();
115        attributes.put(name, value);
116        return this;
117    }
118
119    @NonNull
120    @Override
121    public SimpleAttributedConfigurationNode removeAttribute(@NonNull String name) {
122        attributes.remove(name);
123        return this;
124    }
125
126    @NonNull
127    @Override
128    public SimpleAttributedConfigurationNode setAttributes(@NonNull Map<String, String> attributes) {
129        for (String name : attributes.keySet()) {
130            if (Strings.isNullOrEmpty(name)) {
131                throw new IllegalArgumentException("Attribute name cannot be null/empty");
132            }
133        }
134        this.attributes.clear();
135        if (!attributes.isEmpty()) {
136            attachIfNecessary();
137            this.attributes.putAll(attributes);
138        }
139        return this;
140    }
141
142    @Override
143    public boolean hasAttributes() {
144        return !attributes.isEmpty();
145    }
146
147    @Nullable
148    @Override
149    public String getAttribute(@NonNull String name) {
150        return attributes.get(name);
151    }
152
153    @NonNull
154    @Override
155    public Map<String, String> getAttributes() {
156        return ImmutableMap.copyOf(attributes);
157    }
158
159    @Override
160    public boolean isEmpty() {
161        return super.isEmpty() && attributes.isEmpty();
162    }
163
164    // Methods from superclass overridden to have correct return types
165
166    @Nullable
167    @Override
168    public SimpleAttributedConfigurationNode getParent() {
169        return (SimpleAttributedConfigurationNode) super.getParent();
170    }
171
172    @Override
173    protected SimpleAttributedConfigurationNode createNode(Object path) {
174        return new SimpleAttributedConfigurationNode("element", path, this, getOptions());
175    }
176
177    @NonNull
178    @Override
179    public SimpleAttributedConfigurationNode setValue(@Nullable Object value) {
180        if (value instanceof AttributedConfigurationNode) {
181            AttributedConfigurationNode node = (AttributedConfigurationNode) value;
182            setTagName(node.getTagName());
183            setAttributes(node.getAttributes());
184        }
185        return (SimpleAttributedConfigurationNode) super.setValue(value);
186    }
187
188    @NonNull
189    @Override
190    public SimpleAttributedConfigurationNode mergeValuesFrom(@NonNull ConfigurationNode other) {
191        if (other instanceof AttributedConfigurationNode) {
192            AttributedConfigurationNode node = (AttributedConfigurationNode) other;
193            setTagName(node.getTagName());
194            for (Map.Entry<String, String> attribute : node.getAttributes().entrySet()) {
195                addAttribute(attribute.getKey(), attribute.getValue());
196            }
197        }
198        return (SimpleAttributedConfigurationNode) super.mergeValuesFrom(other);
199    }
200
201    @NonNull
202    @Override
203    public SimpleAttributedConfigurationNode getNode(@NonNull Object... path) {
204        return (SimpleAttributedConfigurationNode) super.getNode(path);
205    }
206
207    @NonNull
208    @Override
209    @SuppressWarnings("unchecked")
210    public List<? extends SimpleAttributedConfigurationNode> getChildrenList() {
211        return (List<SimpleAttributedConfigurationNode>) super.getChildrenList();
212    }
213
214    @NonNull
215    @Override
216    @SuppressWarnings("unchecked")
217    public Map<Object, ? extends SimpleAttributedConfigurationNode> getChildrenMap() {
218        return (Map<Object, SimpleAttributedConfigurationNode>) super.getChildrenMap();
219    }
220
221    @NonNull
222    @Override
223    @Deprecated
224    public SimpleAttributedConfigurationNode getAppendedNode() {
225        return (SimpleAttributedConfigurationNode) super.getAppendedNode();
226    }
227
228    @NonNull
229    @Override
230    public SimpleAttributedConfigurationNode appendListNode() {
231        return (SimpleAttributedConfigurationNode) super.appendListNode();
232    }
233
234    @NonNull
235    @Override
236    public SimpleAttributedConfigurationNode copy() {
237        return copy(null);
238    }
239
240    @NonNull
241    @Override
242    protected SimpleAttributedConfigurationNode copy(@Nullable SimpleConfigurationNode parent) {
243        SimpleAttributedConfigurationNode copy = new SimpleAttributedConfigurationNode(this.tagName, parent, this);
244        copy.attributes.putAll(this.attributes);
245        getComment().ifPresent(copy::setComment);
246        return copy;
247    }
248
249    @Override
250    public @NonNull SimpleAttributedConfigurationNode setComment(@Nullable String comment) {
251        return (SimpleAttributedConfigurationNode) super.setComment(comment);
252    }
253
254    @Override
255    public boolean equals(Object o) {
256        if (this == o) return true;
257        if (!(o instanceof SimpleAttributedConfigurationNode)) return false;
258        if (!super.equals(o)) return false;
259        SimpleAttributedConfigurationNode that = (SimpleAttributedConfigurationNode) o;
260        return tagName.equals(that.tagName) && attributes.equals(that.attributes);
261    }
262
263    @Override
264    public int hashCode() {
265        int result = super.hashCode();
266        result = 31 * result + tagName.hashCode();
267        result = 31 * result + attributes.hashCode();
268        return result;
269    }
270
271    @Override
272    public String toString() {
273        return "SimpleAttributedConfigurationNode{" +
274                "super=" + super.toString() + ", " +
275                "tagName=" + tagName + ", " +
276                "attributes=" + attributes +
277                '}';
278    }
279}