tmf: Fix TmfEventField#equals()
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / event / TmfEventField.java
1 /*******************************************************************************
2 * Copyright (c) 2009, 2015 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are made
5 * available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
11 * Francois Chouinard - Updated as per TMF Event Model 1.0
12 * Alexandre Montplaisir - Removed Cloneable, made immutable
13 * Patrick Tasse - Remove getSubField
14 *******************************************************************************/
15
16 package org.eclipse.tracecompass.tmf.core.event;
17
18 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
19
20 import java.util.Collection;
21 import java.util.Map;
22
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.eclipse.tracecompass.common.core.NonNullUtils;
26 import org.eclipse.tracecompass.common.core.ObjectUtils;
27
28 import com.google.common.base.Joiner;
29 import com.google.common.collect.ImmutableMap;
30
31 /**
32 * A basic implementation of ITmfEventField.
33 * <p>
34 * Non-value fields are structural (i.e. used to represent the event structure
35 * including optional fields) while the valued fields are actual event fields.
36 *
37 * @version 1.0
38 * @author Francois Chouinard
39 *
40 * @see ITmfEvent
41 * @see ITmfEventType
42 */
43 public class TmfEventField implements ITmfEventField {
44
45 // ------------------------------------------------------------------------
46 // Attributes
47 // ------------------------------------------------------------------------
48
49 private final @NonNull String fName;
50 private final @Nullable Object fValue;
51 private final @NonNull Map<String, ITmfEventField> fFields;
52
53 // ------------------------------------------------------------------------
54 // Constructors
55 // ------------------------------------------------------------------------
56
57 /**
58 * Full constructor
59 *
60 * @param name
61 * the event field id
62 * @param value
63 * the event field value
64 * @param fields
65 * the list of subfields
66 * @throws IllegalArgumentException
67 * If 'name' is null, or if 'fields' has duplicate field names.
68 */
69 public TmfEventField(@NonNull String name, @Nullable Object value, @Nullable ITmfEventField[] fields) {
70 fName = name;
71 fValue = value;
72
73 if (fields == null) {
74 fFields = checkNotNull(ImmutableMap.<String, ITmfEventField> of());
75 } else {
76 /* Java 8 streams will make this even more simple! */
77 ImmutableMap.Builder<String, ITmfEventField> mapBuilder = new ImmutableMap.Builder<>();
78 for (ITmfEventField field : fields) {
79 final String curName = field.getName();
80 mapBuilder.put(curName, field);
81 }
82 fFields = checkNotNull(mapBuilder.build());
83 }
84 }
85
86 /**
87 * Copy constructor
88 *
89 * @param field the other event field
90 */
91 public TmfEventField(final TmfEventField field) {
92 if (field == null) {
93 throw new IllegalArgumentException();
94 }
95 fName = field.fName;
96 fValue = field.fValue;
97 fFields = field.fFields;
98 }
99
100 // ------------------------------------------------------------------------
101 // ITmfEventField
102 // ------------------------------------------------------------------------
103
104 @Override
105 public String getName() {
106 return fName;
107 }
108
109 @Override
110 public Object getValue() {
111 return fValue;
112 }
113
114 @Override
115 public final Collection<String> getFieldNames() {
116 return checkNotNull(fFields.keySet());
117 }
118
119 @Override
120 public final Collection<ITmfEventField> getFields() {
121 return checkNotNull(fFields.values());
122 }
123
124 @Override
125 public ITmfEventField getField(final String... path) {
126 if (path.length == 1) {
127 return fFields.get(path[0]);
128 }
129 ITmfEventField field = this;
130 for (String name : path) {
131 field = field.getField(name);
132 if (field == null) {
133 return null;
134 }
135 }
136 return field;
137 }
138
139 // ------------------------------------------------------------------------
140 // Operations
141 // ------------------------------------------------------------------------
142
143 /**
144 * Create a root field from a list of labels.
145 *
146 * @param labels the list of labels
147 * @return the (flat) root list
148 */
149 public static final ITmfEventField makeRoot(final String[] labels) {
150 final ITmfEventField[] fields = new ITmfEventField[labels.length];
151 for (int i = 0; i < labels.length; i++) {
152 String label = checkNotNull(labels[i]);
153 fields[i] = new TmfEventField(label, null, null);
154 }
155 // Return a new root field;
156 return new TmfEventField(ITmfEventField.ROOT_FIELD_ID, null, fields);
157 }
158
159 // ------------------------------------------------------------------------
160 // Object
161 // ------------------------------------------------------------------------
162
163 @Override
164 public int hashCode() {
165 final int prime = 31;
166 int result = 1;
167 result = prime * result + getName().hashCode();
168 result = prime * result + ObjectUtils.deepHashCode(getValue());
169 result = prime * result + fFields.hashCode();
170 return result;
171 }
172
173 @Override
174 public boolean equals(final Object obj) {
175 if (this == obj) {
176 return true;
177 }
178 if (obj == null) {
179 return false;
180 }
181
182 /* We only consider equals fields of the exact same class. */
183 if (!(this.getClass().equals(obj.getClass()))) {
184 return false;
185 }
186
187 final TmfEventField other = (TmfEventField) obj;
188
189 /* Check that the field names are the same. */
190 if (!NonNullUtils.equalsNullable(getName(), other.getName())) {
191 return false;
192 }
193
194 /*
195 * Check that the field values are the same. We use ObjectUtils to
196 * handle cases where the Object values may be primitive and/or nested
197 * arrays.
198 */
199 if (!ObjectUtils.deepEquals(this.getValue(), other.getValue())) {
200 return false;
201 }
202
203 /* Check that sub-fields are the same. */
204 if (!fFields.equals(other.fFields)) {
205 return false;
206 }
207
208 return true;
209 }
210
211 @Override
212 public String toString() {
213 StringBuilder ret = new StringBuilder();
214 if (fName.equals(ITmfEventField.ROOT_FIELD_ID)) {
215 /*
216 * If this field is a top-level "field container", we will print its
217 * sub-fields directly.
218 */
219 appendSubFields(ret);
220
221 } else {
222 /* The field has its own values */
223 ret.append(fName);
224 ret.append('=');
225 ret.append(fValue);
226
227 if (!fFields.isEmpty()) {
228 /*
229 * In addition to its own name/value, this field also has
230 * sub-fields.
231 */
232 ret.append(" ["); //$NON-NLS-1$
233 appendSubFields(ret);
234 ret.append(']');
235 }
236 }
237 return ret.toString();
238 }
239
240 private void appendSubFields(StringBuilder sb) {
241 Joiner joiner = Joiner.on(", ").skipNulls(); //$NON-NLS-1$
242 sb.append(joiner.join(getFields()));
243 }
244
245 @Override
246 public String getFormattedValue() {
247 return getValue().toString();
248 }
249
250 }
This page took 0.03716 seconds and 6 git commands to generate.