Add method to get a track by a derivative ID.
[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         /** The properties of the data container. */
39         private final Properties properties = new Properties();
40
41         /**
42          * Creates a new data container with the given ID.
43          *
44          * @param id
45          *            The ID of the data container
46          */
47         protected DefaultBase(String id) {
48                 this.id = id;
49         }
50
51         //
52         // ACCESSORS
53         //
54
55         /**
56          * {@inheritDoc}
57          */
58         @Override
59         public String getId() {
60                 return id;
61         }
62
63         /**
64          * {@inheritDoc}
65          */
66         @Override
67         public Properties getProperties() {
68                 return properties;
69         }
70
71         /**
72          * Returns whether the data container contains an attribute with the given
73          * name.
74          *
75          * @param name
76          *            The name of the attribute to check for
77          * @return {@code true} if this data container contains an attribute with
78          *         the given name, {@code false} otherwise
79          */
80         protected boolean hasValue(String name) {
81                 return attributes.containsKey(name);
82         }
83
84         /**
85          * Returns the value of the attribute with the given name. If no value for
86          * the given attribute exists, one will be created, stored, and returned.
87          *
88          * @param <T>
89          *            The type of the value to return
90          * @param name
91          *            The name of the attribute to get
92          * @param clazz
93          *            The class of the value
94          * @return The value of the attribute
95          */
96         @SuppressWarnings({ "unchecked" })
97         protected <T> Value<T> getValue(String name, Class<T> clazz) {
98                 if (!attributes.containsKey(name)) {
99                         attributes.put(name, new Value<T>());
100                 }
101                 return (Value<T>) attributes.get(name);
102         }
103
104         /**
105          * Returns whether any of the attributes of this data container is dirty,
106          * i.e. it was modified after its initial inception.
107          *
108          * @return {@code true} if any attribute of this data container was modified
109          */
110         protected boolean isDirty() {
111                 for (Value<?> value : attributes.values()) {
112                         if (value.isDirty()) {
113                                 return true;
114                         }
115                 }
116                 return false;
117         }
118
119         //
120         // OBJECT METHODS
121         //
122
123         /**
124          * {@inheritDoc}
125          */
126         @Override
127         public int hashCode() {
128                 return getId().hashCode();
129         }
130
131         /**
132          * {@inheritDoc}
133          */
134         @Override
135         public boolean equals(Object obj) {
136                 if (!(obj instanceof Base)) {
137                         return false;
138                 }
139                 return getId().equals(((Base) obj).getId());
140         }
141
142         /**
143          * {@inheritDoc}
144          */
145         @Override
146         public String toString() {
147                 StringBuilder stringBuilder = new StringBuilder();
148                 stringBuilder.append(getClass().getName());
149                 stringBuilder.append('[').append("id=").append(getId());
150                 for (Entry<String, Value<?>> attributeEntry : attributes.entrySet()) {
151                         stringBuilder.append(',').append(attributeEntry.getKey()).append('=').append(attributeEntry.getValue().get());
152                 }
153                 stringBuilder.append(']');
154                 return stringBuilder.toString();
155         }
156
157         /**
158          * Container for an attribute value stored in a data container.
159          *
160          * @see DefaultBase#attributes
161          * @param <T>
162          *            The type of the value
163          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
164          */
165         protected static class Value<T> {
166
167                 /** The original value. */
168                 private T original;
169
170                 /** Whether the original value has been set. */
171                 private boolean originalSet;
172
173                 /** The current value. */
174                 private T current;
175
176                 //
177                 // ACCESSORS
178                 //
179
180                 /**
181                  * Returns the current value.
182                  *
183                  * @return The current value
184                  */
185                 public T get() {
186                         return current;
187                 }
188
189                 /**
190                  * Sets the current value. If no original value has yet been set, the
191                  * given value is also stored as original value.
192                  *
193                  * @param value
194                  *            The value to set
195                  * @return This value
196                  */
197                 public Value<T> set(T value) {
198                         if (!originalSet) {
199                                 original = value;
200                                 originalSet = true;
201                         }
202                         current = value;
203                         return this;
204                 }
205
206                 /**
207                  * Returns whether the value of this value does not equal its original
208                  * value.
209                  *
210                  * @return {@code true} if the current value is not equal to the
211                  *         original value, {@code false} if it is equal to the original
212                  *         value
213                  */
214                 public boolean isDirty() {
215                         return (original != null) ? !original.equals(current) : current != null;
216                 }
217
218                 /**
219                  * Commits the current value, i.e. sets the original value to the
220                  * current value.
221                  *
222                  * @return This value
223                  */
224                 public Value<T> commit() {
225                         original = current;
226                         return this;
227                 }
228
229         }
230
231 }