• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kit-data-manager / pit-service / #552

29 Jul 2025 01:16PM UTC coverage: 77.908% (+5.2%) from 72.712%
#552

Pull #264

github

web-flow
Merge 0f68003b1 into 201395eb2
Pull Request #264: Development branch for v3.0.0

654 of 791 new or added lines in 25 files covered. (82.68%)

3 existing lines in 2 files now uncovered.

1065 of 1367 relevant lines covered (77.91%)

0.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.35
/src/main/java/edu/kit/datamanager/pit/domain/PIDRecord.java
1
/*
2
 * Copyright (c) 2025 Karlsruhe Institute of Technology.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package edu.kit.datamanager.pit.domain;
18

19
import com.fasterxml.jackson.annotation.JsonIgnore;
20
import edu.kit.datamanager.entities.EtagSupport;
21
import edu.kit.datamanager.pit.pidsystem.impl.local.PidDatabaseObject;
22

23
import java.util.*;
24
import java.util.stream.Collectors;
25

26
/**
27
 * The internal representation for a PID record, offering methods to manipulate
28
 * the record.
29
 * <p>
30
 * While other representations exist, they are only used for easier database
31
 * communication or representation for the outside. In contrast, this is the
32
 * internal representation offering methods for manipulation.
33
 */
34
public class PIDRecord implements EtagSupport, Cloneable {
35

36
    private String pid = "";
1✔
37

38
    private Map<String, List<PIDRecordEntry>> entries = new HashMap<>();
1✔
39

40
    /**
41
     * Creates an empty record without PID.
42
     */
43
    public PIDRecord() {
1✔
44
    }
1✔
45

46
    /**
47
     * Creates a record with the same content as the given representation.
48
     *
49
     * @param dbo the given record representation.
50
     */
51
    public PIDRecord(PidDatabaseObject dbo) {
1✔
52
        this.setPid(dbo.getPid());
1✔
53
        dbo.getEntries().forEach(
1✔
54
                (key, valueList) -> valueList.forEach(
1✔
55
                        value -> this.addEntry(key, value)));
1✔
56
    }
1✔
57

58
    public PIDRecord(SimplePidRecord rec) {
1✔
59
        this.entries = new HashMap<>();
1✔
60
        for (SimplePair pair : rec.getPairs()) {
1✔
61
            this.addEntry(pair.getKey(), "", pair.getValue());
1✔
62
        }
1✔
63
    }
1✔
64

65
    /**
66
     * Convenience setter / builder method.
67
     *
68
     * @param pid the pid to set in this object.
69
     * @return this object (builder method).
70
     */
71
    public PIDRecord withPID(String pid) {
72
        this.setPid(pid);
1✔
73
        return this;
1✔
74
    }
75

76
    public String getPid() {
77
        return pid;
1✔
78
    }
79

80
    public void setPid(String pid) {
81
        this.pid = pid;
1✔
82
    }
1✔
83

84
    public Map<String, List<PIDRecordEntry>> getEntries() {
85
        return entries;
1✔
86
    }
87

88
    /**
89
     * Sets the entries of this record.
90
     *
91
     * @param entries the entries to set.
92
     */
93
    public void setEntries(Map<String, List<PIDRecordEntry>> entries) {
94
        this.entries = entries;
1✔
95
    }
1✔
96

97
    @JsonIgnore
98
    public Set<SimplePair> getSimpleEntries() {
99
        return this.entries
1✔
100
                .entrySet()
1✔
101
                .stream()
1✔
102
                .flatMap(
1✔
103
                        entry -> entry.getValue().stream()
1✔
104
                                .map(complexPair -> new SimplePair(complexPair.getKey(), complexPair.getValue())))
1✔
105
                .collect(Collectors.toSet());
1✔
106
    }
107

108
    public void addEntry(String propertyIdentifier, String propertyValue) {
109
        this.addEntry(propertyIdentifier, "", propertyValue);
1✔
110
    }
1✔
111

112
    /**
113
     * Adds a new key-name-value triplet.
114
     *
115
     * @param propertyIdentifier the key/type PID.
116
     * @param propertyName       the human-readable name for the given key/type.
117
     * @param propertyValue      the value to this key/type.
118
     */
119
    public void addEntry(String propertyIdentifier, String propertyName, String propertyValue) {
120
        if (propertyIdentifier.isEmpty()) {
1✔
121
            throw new IllegalArgumentException("The identifier of a property may not be empty!");
1✔
122
        }
123
        PIDRecordEntry entry = new PIDRecordEntry();
1✔
124
        entry.setKey(propertyIdentifier);
1✔
125
        entry.setName(propertyName);
1✔
126
        entry.setValue(propertyValue);
1✔
127

128
        this.entries
1✔
129
                .computeIfAbsent(propertyIdentifier, key -> new ArrayList<>())
1✔
130
                .add(entry);
1✔
131
    }
1✔
132

133
    /**
134
     * Sets the name for a given key/type in all available pairs.
135
     *
136
     * @param propertyIdentifier the key/type.
137
     * @param name               the new name.
138
     */
139
    @JsonIgnore
140
    public void setPropertyName(String propertyIdentifier, String name) {
141
        List<PIDRecordEntry> propertyEntries = this.entries.get(propertyIdentifier);
×
142
        if (propertyEntries == null) {
×
143
            throw new IllegalArgumentException(
×
144
                    "Property identifier not listed in this record: " + propertyIdentifier);
145
        }
146
        for (PIDRecordEntry entry : propertyEntries) {
×
147
            entry.setName(name);
×
148
        }
×
149
    }
×
150

151
    /**
152
     * Check if there is a pair or triplet containing the given property (key/type)
153
     * is availeble in this record.
154
     *
155
     * @param propertyIdentifier the key/type to search for.
156
     * @return true, if the property/key/type is present.
157
     */
158
    public boolean hasProperty(String propertyIdentifier) {
159
        return entries.containsKey(propertyIdentifier);
1✔
160
    }
161

162
    /**
163
     * Removes all properties that are not listed in the given collection.
164
     *
165
     * @param propertiesToKeep a collection of property identifiers to keep.
166
     */
167
    public void removePropertiesNotListed(Collection<String> propertiesToKeep) {
168
        entries.keySet().removeIf(propID -> !propertiesToKeep.contains(propID));
1✔
169
    }
1✔
170

171
    public void removeAllValuesOf(String attribute) {
172
        this.entries.remove(attribute);
1✔
173
    }
1✔
174

175
    /**
176
     * Get all properties contained in this record.
177
     *
178
     * @return al contained properties.
179
     */
180
    @JsonIgnore
181
    public Set<String> getPropertyIdentifiers() {
182
        return entries.keySet();
1✔
183
    }
184

185
    /**
186
     * Get the value of the first element in case there are multiple elements
187
     * for the provided propertyIndentifier.
188
     */
189
    public String getPropertyValue(String propertyIdentifier) {
190
        List<PIDRecordEntry> entry = entries.get(propertyIdentifier);
1✔
191
        if (entry == null) {
1✔
192
            return "";
×
193
        }
194
        return entry.getFirst().getValue();
1✔
195
    }
196

197
    /**
198
     * Get all values of a given property.
199
     *
200
     * @param propertyIdentifier the given property identifier.
201
     * @return all values of the given property.
202
     */
203
    public String[] getPropertyValues(String propertyIdentifier) {
204
        List<PIDRecordEntry> entry = entries.get(propertyIdentifier);
1✔
205
        if (entry == null) {
1✔
206
            return new String[]{};
1✔
207
        }
208

209
        List<String> values = new ArrayList<>();
1✔
210
        for (PIDRecordEntry e : entry) {
1✔
211
            values.add(e.getValue());
1✔
212
        }
1✔
213
        return values.toArray(new String[]{});
1✔
214
    }
215

216
    @Override
217
    public int hashCode() {
218
        final int prime = 31;
1✔
219
        int result = 1;
1✔
220
        result = prime * result + ((pid == null) ? 0 : pid.hashCode());
1✔
221
        Set<SimplePair> simpleEntries = this.getSimpleEntries();
1✔
222
        result = prime * result + ((simpleEntries == null) ? 0 : simpleEntries.hashCode());
1✔
223
        return result;
1✔
224
    }
225

226
    /**
227
     * Checks if two PIDRecords are equivalent.
228
     * <p>
229
     * - Ignores the name attribute: Only keys and values matter.
230
     * - Ignores order of keys or values
231
     */
232
    @Override
233
    public boolean equals(Object obj) {
234
        if (this == obj) {
1✔
235
            return true;
1✔
236
        }
237
        if (obj == null) {
1✔
NEW
238
            return false;
×
239
        }
240
        if (getClass() != obj.getClass()) {
1✔
NEW
241
            return false;
×
242
        }
243

244
        PIDRecord other = (PIDRecord) obj;
1✔
245
        boolean isThisPidEmpty = pid == null || pid.isBlank();
1✔
246
        boolean isOtherPidEmpty = other.pid == null || other.pid.isBlank();
1✔
247
        boolean isBothPidEmpty = isThisPidEmpty && isOtherPidEmpty;
1✔
248
        boolean equalPIDs = isBothPidEmpty || (this.pid != null && this.pid.equals(other.pid));
1✔
249

250
        if (!equalPIDs) {
1✔
251
            return false;
1✔
252
        }
253

254
        // this ignores attributes order, names, and even duplicates
255
        return this.getSimpleEntries().equals(other.getSimpleEntries());
1✔
256
    }
257

258
    @Override
259
    public String toString() {
260
        return "PIDRecord [pid=" + pid + ", entries=" + entries + "]";
1✔
261
    }
262

263
    /**
264
     * Calculates an etag for a record.
265
     *
266
     * @return an etag, which is independent of any order or duplicates in the
267
     * entries.
268
     */
269
    @JsonIgnore
270
    @Override
271
    public String getEtag() {
272
        return Integer.toString(this.hashCode());
1✔
273
    }
274

275
    @Override
276
    public PIDRecord clone() {
277
        try {
278
            PIDRecord clone = (PIDRecord) super.clone();
1✔
279
            clone.pid = this.pid;
1✔
280
            clone.entries = new HashMap<>();
1✔
281
            for (Map.Entry<String, List<PIDRecordEntry>> entry : this.entries.entrySet()) {
1✔
282
                List<PIDRecordEntry> entryList = new ArrayList<>();
1✔
283
                for (PIDRecordEntry e : entry.getValue()) {
1✔
284
                    entryList.add(e.clone());
1✔
285
                }
1✔
286
                clone.entries.put(entry.getKey(), entryList);
1✔
287
            }
1✔
288
            return clone;
1✔
NEW
289
        } catch (CloneNotSupportedException e) {
×
NEW
290
            throw new AssertionError();
×
291
        }
292
    }
293
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc