001/*
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.model.api;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.util.CoverageIgnore;
024import org.hl7.fhir.instance.model.api.IBase;
025
026import java.io.Serializable;
027import java.util.ArrayList;
028import java.util.Collection;
029import java.util.Iterator;
030import java.util.LinkedHashSet;
031import java.util.List;
032import java.util.Set;
033
034/**
035 * A collection of tags present on a single resource. TagList is backed by a {@link LinkedHashSet}, so the order of
036 * added tags will be consistent, but duplicates will not be preserved.
037 *
038 * <p>
039 * <b>Thread safety:</b> This class is not thread safe
040 * </p>
041 */
042public class TagList implements Set<Tag>, Serializable, IBase {
043
044        public static final String ATTR_CATEGORY = "category";
045        public static final String ELEMENT_NAME = "TagList";
046
047        public static final String ELEMENT_NAME_LC = ELEMENT_NAME.toLowerCase();
048        private static final long serialVersionUID = 1L;
049        private transient List<Tag> myOrderedTags;
050        private LinkedHashSet<Tag> myTagSet = new LinkedHashSet<Tag>();
051
052        /**
053         * Constructor
054         */
055        public TagList() {
056                super();
057        }
058
059        /**
060         * Copy constructor
061         */
062        public TagList(TagList theTags) {
063                if (theTags != null) {
064                        for (Tag next : theTags) {
065                                add(next);
066                        }
067                }
068        }
069
070        @Override
071        public String toString() {
072                StringBuilder b = new StringBuilder();
073                b.append("TagList[").append(size()).append(" tag(s)]");
074                for (Tag next : this) {
075                        b.append("\n * ").append(next.toString());
076                }
077                return b.toString();
078        }
079
080        @Override
081        public boolean add(Tag theE) {
082                myOrderedTags = null;
083                return myTagSet.add(theE);
084        }
085
086        @Override
087        public boolean addAll(Collection<? extends Tag> theC) {
088                myOrderedTags = null;
089                return myTagSet.addAll(theC);
090        }
091
092        /**
093         * @deprecated Tags wil become immutable in a future release of HAPI, so {@link #addTag(String, String, String)}
094         *             should be used instead
095         */
096        @Deprecated
097        public Tag addTag() {
098                myOrderedTags = null;
099                return addTag(null, null, null);
100        }
101
102        /**
103         * Add a new tag instance
104         *
105         * @param theScheme
106         *           The tag scheme (the system)
107         * @param theTerm
108         *           The tag term (the code)
109         * @return Returns the newly created tag instance. Note that the tag is added to the list by this method, so you
110         *         generally do not need to interact directly with the added tag.
111         */
112        public Tag addTag(String theScheme, String theTerm) {
113                Tag retVal = new Tag(theScheme, theTerm);
114                add(retVal);
115                myOrderedTags = null;
116                return retVal;
117        }
118
119        /**
120         * Add a new tag instance
121         *
122         * @param theScheme
123         *           The tag scheme
124         * @param theTerm
125         *           The tag term
126         * @param theLabel
127         *           The tag label
128         * @return Returns the newly created tag instance. Note that the tag is added to the list by this method, so you
129         *         generally do not need to interact directly with the added tag.
130         */
131        public Tag addTag(String theScheme, String theTerm, String theLabel) {
132                Tag retVal = new Tag(theScheme, theTerm, theLabel);
133                add(retVal);
134                myOrderedTags = null;
135                return retVal;
136        }
137
138        @Override
139        public void clear() {
140                myOrderedTags = null;
141                myTagSet.clear();
142        }
143
144        @Override
145        public boolean contains(Object theO) {
146                return myTagSet.contains(theO);
147        }
148
149        @Override
150        public boolean containsAll(Collection<?> theC) {
151                return myTagSet.containsAll(theC);
152        }
153
154        @Override
155        public boolean equals(Object obj) {
156                if (this == obj) return true;
157                if (obj == null) return false;
158                if (getClass() != obj.getClass()) return false;
159                TagList other = (TagList) obj;
160                if (myTagSet == null) {
161                        if (other.myTagSet != null) return false;
162                } else if (!myTagSet.equals(other.myTagSet)) return false;
163                return true;
164        }
165
166        /**
167         * Returns the tag at a given index - Note that the TagList is backed by a {@link LinkedHashSet}, so the order of
168         * added tags will be consistent, but duplicates will not be preserved.
169         */
170        public Tag get(int theIndex) {
171                if (myOrderedTags == null) {
172                        myOrderedTags = new ArrayList<Tag>();
173                        for (Tag next : myTagSet) {
174                                myOrderedTags.add(next);
175                        }
176                }
177                return myOrderedTags.get(theIndex);
178        }
179
180        public Tag getTag(String theScheme, String theTerm) {
181                for (Tag next : this) {
182                        if (theScheme.equals(next.getScheme()) && theTerm.equals(next.getTerm())) {
183                                return next;
184                        }
185                }
186                return null;
187        }
188
189        public List<Tag> getTagsWithScheme(String theScheme) {
190                ArrayList<Tag> retVal = new ArrayList<Tag>();
191                for (Tag next : this) {
192                        if (theScheme.equals(next.getScheme())) {
193                                retVal.add(next);
194                        }
195                }
196                return retVal;
197        }
198
199        @Override
200        public int hashCode() {
201                return myTagSet.hashCode();
202        }
203
204        @Override
205        public boolean isEmpty() {
206                for (Tag next : myTagSet) {
207                        if (next.isEmpty() == false) {
208                                return false;
209                        }
210                }
211                return true;
212        }
213
214        @Override
215        public Iterator<Tag> iterator() {
216                return myTagSet.iterator();
217        }
218
219        @Override
220        public boolean remove(Object theO) {
221                myOrderedTags = null;
222                return myTagSet.remove(theO);
223        }
224
225        @Override
226        public boolean removeAll(Collection<?> theC) {
227                myOrderedTags = null;
228                return myTagSet.removeAll(theC);
229        }
230
231        @Override
232        public boolean retainAll(Collection<?> theC) {
233                myOrderedTags = null;
234                return myTagSet.retainAll(theC);
235        }
236
237        @Override
238        public int size() {
239                return myTagSet.size();
240        }
241
242        @Override
243        public Object[] toArray() {
244                return myTagSet.toArray();
245        }
246
247        @Override
248        public <T> T[] toArray(T[] theA) {
249                return myTagSet.toArray(theA);
250        }
251
252        /**
253         * Returns false
254         */
255        @Override
256        @CoverageIgnore
257        public boolean hasFormatComment() {
258                return false;
259        }
260
261        /**
262         * NOT SUPPORTED - Throws {@link UnsupportedOperationException}
263         */
264        @Override
265        @CoverageIgnore
266        public List<String> getFormatCommentsPre() {
267                throw new UnsupportedOperationException(Msg.code(1895));
268        }
269
270        /**
271         * NOT SUPPORTED - Throws {@link UnsupportedOperationException}
272         */
273        @Override
274        @CoverageIgnore
275        public List<String> getFormatCommentsPost() {
276                throw new UnsupportedOperationException(Msg.code(1896));
277        }
278
279        @Override
280        public Object getUserData(String theName) {
281                throw new UnsupportedOperationException(Msg.code(1897));
282        }
283
284        @Override
285        public void setUserData(String theName, Object theValue) {
286                throw new UnsupportedOperationException(Msg.code(1898));
287        }
288}