345ebd29d972af3562a4e223fe4a70475d0f7095
[demoscenemusic.git] / src / main / java / net / pterodactylus / demoscenemusic / data / DefaultBase.java
1 /*
2  * DemosceneMusic - Base.java - Copyright © 2012 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.demoscenemusic.data;
19
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.Map.Entry;
23
24 /**
25  * Base implementation of a data container. It stores the ID of a container and
26  * can contain arbitrary attributes.
27  *
28  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
29  */
30 public class DefaultBase implements Base {
31
32         /** The ID of the data container. */
33         private final String id;
34
35         /** The attributes of the data container. */
36         private final Map<String, Value<?>> attributes = new HashMap<String, Value<?>>();
37
38         /**
39          * Creates a new data container with the given ID.
40          *
41          * @param id
42          *            The ID of the data container
43          */
44         protected DefaultBase(String id) {
45                 this.id = id;
46         }
47
48         //
49         // ACCESSORS
50         //
51
52         /**
53          * {@inheritDoc}
54          */
55         @Override
56         public String getId() {
57                 return id;
58         }
59
60         /**
61          * Returns whether the data container contains an attribute with the given
62          * name.
63          *
64          * @param name
65          *            The name of the attribute to check for
66          * @return {@code true} if this data container contains an attribute with
67          *         the given name, {@code false} otherwise
68          */
69         protected boolean hasValue(String name) {
70                 return attributes.containsKey(name);
71         }
72
73         /**
74          * Returns the value of the attribute with the given name. If no value for
75          * the given attribute exists, one will be created, stored, and returned.
76          *
77          * @param <T>
78          *            The type of the value to return
79          * @param name
80          *            The name of the attribute to get
81          * @param clazz
82          *            The class of the value
83          * @return The value of the attribute
84          */
85         @SuppressWarnings({ "unchecked" })
86         protected <T> Value<T> getValue(String name, Class<T> clazz) {
87                 if (!attributes.containsKey(name)) {
88                         attributes.put(name, new Value<T>());
89                 }
90                 return (Value<T>) attributes.get(name);
91         }
92
93         /**
94          * Returns whether any of the attributes of this data container is dirty,
95          * i.e. it was modified after its initial inception.
96          *
97          * @return {@code true} if any attribute of this data container was modified
98          */
99         protected boolean isDirty() {
100                 for (Value<?> value : attributes.values()) {
101                         if (value.isDirty()) {
102                                 return true;
103                         }
104                 }
105                 return false;
106         }
107
108         //
109         // OBJECT METHODS
110         //
111
112         /**
113          * {@inheritDoc}
114          */
115         @Override
116         public int hashCode() {
117                 return getId().hashCode();
118         }
119
120         /**
121          * {@inheritDoc}
122          */
123         @Override
124         public boolean equals(Object obj) {
125                 if (!(obj instanceof Base)) {
126                         return false;
127                 }
128                 return getId().equals(((Base) obj).getId());
129         }
130
131         /**
132          * {@inheritDoc}
133          */
134         @Override
135         public String toString() {
136                 StringBuilder stringBuilder = new StringBuilder();
137                 stringBuilder.append(getClass().getName());
138                 stringBuilder.append('[').append("id=").append(getId());
139                 for (Entry<String, Value<?>> attributeEntry : attributes.entrySet()) {
140                         stringBuilder.append(',').append(attributeEntry.getKey()).append('=').append(attributeEntry.getValue().get());
141                 }
142                 stringBuilder.append(']');
143                 return stringBuilder.toString();
144         }
145
146         /**
147          * Container for an attribute value stored in a data container.
148          *
149          * @see DefaultBase#attributes
150          * @param <T>
151          *            The type of the value
152          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
153          */
154         protected static class Value<T> {
155
156                 /** The original value. */
157                 private T original;
158
159                 /** Whether the original value has been set. */
160                 private boolean originalSet;
161
162                 /** The current value. */
163                 private T current;
164
165                 //
166                 // ACCESSORS
167                 //
168
169                 /**
170                  * Returns the current value.
171                  *
172                  * @return The current value
173                  */
174                 public T get() {
175                         return current;
176                 }
177
178                 /**
179                  * Sets the current value. If no original value has yet been set, the
180                  * given value is also stored as original value.
181                  *
182                  * @param value
183                  *            The value to set
184                  * @return This value
185                  */
186                 public Value<T> set(T value) {
187                         if (!originalSet) {
188                                 original = value;
189                                 originalSet = true;
190                         }
191                         current = value;
192                         return this;
193                 }
194
195                 /**
196                  * Returns whether the value of this value does not equal its original
197                  * value.
198                  *
199                  * @return {@code true} if the current value is not equal to the
200                  *         original value, {@code false} if it is equal to the original
201                  *         value
202                  */
203                 public boolean isDirty() {
204                         return (original != null) ? !original.equals(current) : current != null;
205                 }
206
207                 /**
208                  * Commits the current value, i.e. sets the original value to the
209                  * current value.
210                  *
211                  * @return This value
212                  */
213                 public Value<T> commit() {
214                         original = current;
215                         return this;
216                 }
217
218         }
219
220 }