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