View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.datamodel.classifications2;
20  
21  import java.io.Serializable;
22  import java.text.MessageFormat;
23  import java.util.Locale;
24  import java.util.StringTokenizer;
25  import java.util.regex.Pattern;
26  
27  import org.mycore.common.MCRException;
28  
29  import com.fasterxml.jackson.annotation.JsonCreator;
30  import com.fasterxml.jackson.annotation.JsonFormat;
31  import com.fasterxml.jackson.annotation.JsonValue;
32  
33  import jakarta.persistence.Access;
34  import jakarta.persistence.AccessType;
35  import jakarta.persistence.Basic;
36  import jakarta.persistence.Column;
37  import jakarta.persistence.Embeddable;
38  import jakarta.persistence.Transient;
39  
40  /**
41   * The composite identifier of a MCRCategory. If <code>rootID == ID</code> the
42   * associated MCRCategory instance is a root category (a classification).
43   * 
44   * @author Thomas Scheffler (yagee)
45   * @version $Revision$ $Date: 2008-04-11 09:14:19 +0000 (Fr, 11 Apr
46   *          2008) $
47   * @since 2.0
48   */
49  /**
50   * @author mcradmin
51   *
52   */
53  @Embeddable
54  @Access(AccessType.FIELD)
55  @JsonFormat(shape = JsonFormat.Shape.STRING)
56  public class MCRCategoryID implements Serializable {
57  
58      private static final long serialVersionUID = -5672923571406252855L;
59  
60      private static final Pattern VALID_ID = Pattern.compile("[^:$\\{\\}]+");
61  
62      private static final int ROOT_ID_LENGTH = 32;
63  
64      private static final int CATEG_ID_LENGTH = 128;
65  
66      @Basic
67      @Column(name = "ClassID", length = ROOT_ID_LENGTH, nullable = false, updatable = false)
68      private String rootID;
69  
70      @Basic
71      @Column(name = "CategID", length = CATEG_ID_LENGTH, updatable = false)
72      private String id;
73  
74      private MCRCategoryID() {
75          super();
76      }
77  
78      /**
79       * @param rootID
80       *            aka Classification ID
81       * @param id
82       *            aka Category ID
83       */
84      public MCRCategoryID(String rootID, String id) {
85          this();
86          setID(id);
87          setRootID(rootID);
88      }
89  
90      public static MCRCategoryID rootID(String rootID) {
91          return new MCRCategoryID(rootID, "");
92      }
93  
94      /**
95       * @param categoryId must be in format classificationId:categoryId
96       * @return the {@link MCRCategoryID} if any
97       */
98      @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
99      public static MCRCategoryID fromString(String categoryId) {
100         StringTokenizer tok = new StringTokenizer(categoryId, ":");
101         String rootId = tok.nextToken();
102         if (!tok.hasMoreTokens()) {
103             return rootID(rootId);
104         }
105         String categId = tok.nextToken();
106         if (tok.hasMoreTokens()) {
107             throw new IllegalArgumentException("CategoryId is ambiguous: " + categoryId);
108         }
109         return new MCRCategoryID(rootId, categId);
110     }
111 
112     @Transient
113     public boolean isRootID() {
114         return id == null || id.equals("");
115     }
116 
117     /*
118      * (non-Javadoc)
119      * 
120      * @see java.lang.Object#hashCode()
121      */
122     @Override
123     public int hashCode() {
124         final int prime = 31;
125         int result = 1;
126         result = prime * result + (id == null || id.isEmpty() ? 0 : id.hashCode());
127         result = prime * result + (rootID == null ? 0 : rootID.hashCode());
128         return result;
129     }
130 
131     /*
132      * (non-Javadoc)
133      * 
134      * @see java.lang.Object#equals(java.lang.Object)
135      */
136     @Override
137     public boolean equals(Object obj) {
138         if (this == obj) {
139             return true;
140         }
141         if (obj == null) {
142             return false;
143         }
144         if (getClass() != obj.getClass()) {
145             return false;
146         }
147         final MCRCategoryID other = (MCRCategoryID) obj;
148         if (id == null) {
149             if (other.id != null && other.id.length() > 0) {
150                 return false;
151             }
152         } else if (!id.equals(other.id) && (id.length() > 0 || other.id != null && other.id.length() >= 0)) {
153             return false;
154         }
155         if (rootID == null) {
156             return other.rootID == null;
157         } else {
158             return rootID.equals(other.rootID);
159         }
160     }
161 
162     /**
163      * @return the ID
164      */
165     public String getID() {
166         return id == null ? "" : id;
167     }
168 
169     /**
170      * @param id
171      *            the ID to set
172      */
173     private void setID(String id) {
174         if (id != null && id.length() > 0) {
175             if (!VALID_ID.matcher(id).matches()) {
176                 throw new MCRException("category ID '" + id + "' is invalid and does not match: " + VALID_ID);
177             }
178             if (id.length() > CATEG_ID_LENGTH) {
179                 throw new MCRException(
180                     new MessageFormat("category ID ''{0}'' is more than {1} characters long: {2}", Locale.ROOT)
181                         .format(new Object[] { id, CATEG_ID_LENGTH, id.length() }));
182             }
183         }
184         this.id = id;
185     }
186 
187     /**
188      * @param id
189      *              the ID to check
190      * @return true, if the given String is a valid categoryID
191      */
192     public static boolean isValid(String id) {
193         try {
194             MCRCategoryID.fromString(id);
195             return true;
196         } catch (Exception e) {
197             return false;
198         }
199     }
200 
201     /**
202      * @return the rootID
203      */
204     public String getRootID() {
205         return rootID;
206     }
207 
208     /**
209      * @param rootID
210      *            the rootID to set
211      */
212     private void setRootID(String rootID) {
213         if (!VALID_ID.matcher(rootID).matches()) {
214             throw new MCRException(
215                 new MessageFormat("classification ID ''{0}'' is invalid and does not match: {1}", Locale.ROOT)
216                     .format(new Object[] { rootID, VALID_ID }));
217         }
218         if (rootID.length() > ROOT_ID_LENGTH) {
219             throw new MCRException(String.format(Locale.ENGLISH,
220                 "classification ID ''%s'' is more than %d chracters long: %d", rootID, ROOT_ID_LENGTH,
221                 rootID.length()));
222         }
223         this.rootID = rootID.intern();
224     }
225 
226     /*
227      * (non-Javadoc)
228      * 
229      * @see java.lang.Object#toString()
230      */
231     @Override
232     @JsonValue
233     public String toString() {
234         if (id == null || id.length() == 0) {
235             return rootID;
236         }
237         return rootID + ':' + id;
238     }
239 
240 }