001 /*
002 *
003 * $Revision: 15621 $ $Date: 2009-07-25 08:32:01 +0200 (Sat, 25 Jul 2009) $
004 *
005 * This file is part of *** M y C o R e ***
006 * See http://www.mycore.de/ for details.
007 *
008 * This program is free software; you can use it, redistribute it
009 * and / or modify it under the terms of the GNU General Public License
010 * (GPL) as published by the Free Software Foundation; either version 2
011 * of the License or (at your option) any later version.
012 *
013 * This program is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program, in a file called gpl.txt or license.txt.
020 * If not, write to the Free Software Foundation Inc.,
021 * 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
022 */
023
024 package org.mycore.datamodel.metadata;
025
026 import java.io.UnsupportedEncodingException;
027 import java.net.URLEncoder;
028 import java.text.DecimalFormat;
029 import java.util.HashMap;
030
031 import org.apache.log4j.Logger;
032 import org.mycore.common.MCRConfiguration;
033 import org.mycore.common.MCRException;
034 import org.mycore.datamodel.common.MCRXMLTableManager;
035
036 /**
037 * This class holds all informations and methods to handle the MyCoRe Object ID.
038 * The MyCoRe Object ID is a special ID to identify each metadata object with
039 * three parts, they are the project identifier, the type identifier and a
040 * string with a number.
041 *
042 * @author Jens Kupferschmidt
043 * @author Thomas Scheffler (yagee)
044 * @version $Revision: 15621 $ $Date: 2009-07-25 08:32:01 +0200 (Sat, 25 Jul 2009) $
045 */
046 public final class MCRObjectID {
047 /**
048 * public constant value for the MCRObjectID length
049 */
050 public static final int MAX_LENGTH = 64;
051
052 // configuration values
053 protected static MCRConfiguration CONFIG;
054
055 private static final Logger LOGGER = Logger.getLogger(MCRObjectID.class);
056
057 // counter for the next IDs per project base ID
058 private static HashMap<String, Integer> lastnumber = new HashMap<String, Integer>();
059
060 // data of the ID
061 private String mcr_project_id = null;
062
063 private String mcr_type_id = null;
064
065 private String mcr_id = null;
066
067 private int mcr_number = -1;
068
069 private boolean mcr_valid_id = false;
070
071 private static int number_distance = 1;
072
073 private static String number_pattern = null;
074
075 private static DecimalFormat number_format = null;
076
077 /**
078 * Static method to load the configuration.
079 */
080 static {
081 CONFIG = MCRConfiguration.instance();
082 number_distance = CONFIG.getInt("MCR.Metadata.ObjectID.NumberDistance", 1);
083 number_pattern = CONFIG.getString("MCR.Metadata.ObjectID.NumberPattern", "0000000000");
084 number_format = new DecimalFormat(number_pattern);
085 }
086
087 /**
088 * The constructor for an empty MCRObjectId.
089 */
090 public MCRObjectID() {
091 }
092
093 /**
094 * The constructor for MCRObjectID from a given string.
095 *
096 * @exception MCRException
097 * if the given string is not valid.
098 */
099 public MCRObjectID(String id) throws MCRException {
100 mcr_valid_id = false;
101
102 if (!setID(id)) {
103 throw new MCRException("The ID is not valid: " + id);
104 }
105
106 mcr_valid_id = true;
107 }
108
109 /**
110 * The method set the MCRObjectID from a given base ID string. The number
111 * was computed from this methode. It is the next free number of an item in
112 * the database for the given project ID and type ID.
113 *
114 * @exception MCRException
115 * if the given string is not valid or can't connect to the
116 * MCRXMLTableManager.
117 */
118 public void setNextFreeId() throws MCRException {
119 setNextFreeId(getBase());
120 }
121
122 /**
123 * The method set the MCRObjectID from a given base ID string. A base ID is
124 * <em>project_id</em>_<em>type_id</em>. The number was computed from
125 * this methode. It is the next free number of an item in the database for
126 * the given project ID and type ID.
127 *
128 * @param base_id
129 * the basic ID
130 * @exception MCRException
131 * if the given string is not valid or can't connect to the
132 * MCRXMLTableManager.
133 */
134 public synchronized void setNextFreeId(String base_id) throws MCRException {
135 setNextFreeId(base_id, 0);
136 }
137
138 public synchronized void setNextFreeId(String base_id, int maxInWorkflow) throws MCRException {
139 // check the base_id
140 mcr_valid_id = false;
141
142 if( ! setID( base_id + "_1" ) ) {
143 throw new MCRException("Error in project base string:" + base_id );
144 }
145
146 int last = Math.max(getLastID(base_id).getNumberAsInteger(), maxInWorkflow) + 1;
147 int rest = last % number_distance;
148 if (rest != 0)
149 last += number_distance - rest;
150 lastnumber.put(base_id, last);
151 setID(base_id + "_" + String.valueOf(last));
152 }
153
154 /**
155 * Returns the last ID used or reserved for the given object base type.
156 */
157 public static MCRObjectID getLastID(String base_id) {
158 MCRObjectID oid = new MCRObjectID(base_id + "_1");
159 int last = lastnumber.containsKey(base_id) ? lastnumber.get(base_id) : 0;
160 int stored = MCRXMLTableManager.instance().getHighestStoredID(oid.getProjectId(), oid.getTypeId());
161 oid.setNumber(Math.max(last, stored));
162 return oid;
163 }
164
165 /**
166 * This method set a new type in a existing MCRObjectID.
167 *
168 * @param type
169 * the new type
170 */
171 public final boolean setType(String type) {
172 if (type == null) {
173 return false;
174 }
175
176 String test = type.toLowerCase().intern();
177
178 if (!CONFIG.getBoolean("MCR.Metadata.Type." + test, false)) {
179 return false;
180 }
181
182 mcr_type_id = test;
183
184 return true;
185 }
186
187 /**
188 * This method set a new number in a existing MCRObjectID.
189 *
190 * @param num
191 * the new number
192 */
193 public final boolean setNumber(int num) {
194 if (!mcr_valid_id) {
195 return false;
196 }
197
198 if (num < 0) {
199 return false;
200 }
201
202 mcr_number = num;
203 mcr_id = null;
204
205 return true;
206 }
207
208 /**
209 * This method get the string with <em>project_id</em>. If the ID is not
210 * valid, an empty string was returned.
211 *
212 * @return the string of the project id
213 */
214 public final String getProjectId() {
215 if (!mcr_valid_id) {
216 return "";
217 }
218
219 return mcr_project_id;
220 }
221
222 /**
223 * This method get the string with <em>type_id</em>. If the ID is not
224 * valid, an empty string was returned.
225 *
226 * @return the string of the type id
227 */
228 public final String getTypeId() {
229 if (!mcr_valid_id) {
230 return "";
231 }
232
233 return mcr_type_id;
234 }
235
236 /**
237 * This method get the string with <em>number</em>. If the ID is not
238 * valid, an empty string was returned.
239 *
240 * @return the string of the number
241 */
242 public final String getNumberAsString() {
243 if (!mcr_valid_id) {
244 return "";
245 }
246
247 return number_format.format(mcr_number);
248 }
249
250 /**
251 * This method get the integer with <em>number</em>. If the ID is not
252 * valid, a -1 was returned.
253 *
254 * @return the number as integer
255 */
256 public final int getNumberAsInteger() {
257 if (!mcr_valid_id) {
258 return -1;
259 }
260
261 return mcr_number;
262 }
263
264 /**
265 * This method get the basic string with <em>project_id</em>_
266 * <em>type_id</em>. If the Id is not valid, an empty string was
267 * returned.
268 *
269 * @return the string of the schema name
270 */
271 public String getBase() {
272 if (!mcr_valid_id) {
273 return "";
274 }
275
276 return mcr_project_id + "_" + mcr_type_id;
277 }
278
279 /**
280 * This method get the ID string with <em>project_id</em>_
281 * <em>type_id</em>_<em>number</em>. If the ID is not valid, an empty
282 * string was returned.
283 *
284 * @return the string of the schema name
285 */
286 public String getId() {
287 if (!mcr_valid_id) {
288 return "";
289 }
290
291 if (mcr_id == null) {
292 mcr_id = new StringBuffer(MAX_LENGTH).append(mcr_project_id).append('_').append(mcr_type_id).append('_').append(number_format.format(mcr_number)).toString();
293 }
294
295 return mcr_id;
296 }
297
298 /**
299 * This method return the validation value of a MCRObjectId. The MCRObjectID
300 * is valid if:
301 * <ul>
302 * <li>The syntax of the ID is <em>project_id</em>_<em>type_id</em>_
303 * <em>number</em> as <em>String_String_Integer</em>.
304 * <li>The ID is not longer as MAX_LENGTH.
305 * </ul>
306 *
307 * @return the validation value, true if the MCRObjectId is correct,
308 * otherwise return false
309 */
310 public boolean isValid() {
311 return mcr_valid_id;
312 }
313
314 /**
315 * This method return the validation value of a MCRObjectId and store the
316 * components in this class. The <em>type_id</em> was set to lower case.
317 * The MCRObjectID is valid if:
318 * <ul>
319 * <li>The argument is not null.
320 * <li>The syntax of the ID is <em>project_id</em>_<em>type_id</em>_
321 * <em>number</em> as <em>String_String_Integer</em>.
322 * <li>The ID is not longer as MAX_LENGTH.
323 * <li>The ID has only characters, they must not encoded.
324 * </ul>
325 *
326 * @param id
327 * the MCRObjectId
328 * @return the validation value, true if the MCRObjectId is correct,
329 * otherwise return false
330 */
331 public final boolean setID(String id) {
332 mcr_valid_id = false;
333
334 if ((id == null) || ((id = id.trim()).length() == 0)) {
335 return false;
336 }
337
338 if (id.length() > MAX_LENGTH) {
339 return false;
340 }
341
342 String mcr_id;
343
344 try {
345 mcr_id = URLEncoder.encode(id, CONFIG.getString("MCR.Request.CharEncoding", "UTF-8"));
346 } catch (UnsupportedEncodingException e1) {
347 LOGGER.error("MCR.Request.CharEncoding property does not contain a valid encoding:", e1);
348
349 return false;
350 }
351
352 if (!mcr_id.equals(id)) {
353 return false;
354 }
355
356 int len = mcr_id.length();
357 int i = mcr_id.indexOf("_");
358
359 if (i == -1) {
360 return false;
361 }
362
363 mcr_project_id = mcr_id.substring(0, i).intern();
364
365 int j = mcr_id.indexOf("_", i + 1);
366
367 if (j == -1) {
368 return false;
369 }
370
371 mcr_type_id = mcr_id.substring(i + 1, j).toLowerCase().intern();
372
373 if (!CONFIG.getBoolean("MCR.Metadata.Type." + mcr_type_id.toLowerCase(), false)) {
374 return false;
375 }
376
377 mcr_number = -1;
378
379 try {
380 mcr_number = Integer.parseInt(mcr_id.substring(j + 1, len));
381 } catch (NumberFormatException e) {
382 return false;
383 }
384
385 if (mcr_number < 0) {
386 return false;
387 }
388
389 this.mcr_id = null;
390 mcr_valid_id = true;
391
392 return mcr_valid_id;
393 }
394
395 /**
396 * This method checks the value of a MCRObjectId. The MCRObjectId is valid
397 * if:
398 * <ul>
399 * <li>The argument is not null.
400 * <li>The syntax of the ID is <em>project_id</em>_<em>type_id</em>_
401 * <em>number</em> as <em>String_String_Integer</em>.
402 * <li>The ID is not longer as MAX_LENGTH. >li> The ID has only characters,
403 * they must not encoded.
404 * </ul>
405 *
406 * @param id
407 * the MCRObjectId
408 * @throws MCRException
409 * if ID is not valid
410 */
411 public static void isValidOrDie(String id) {
412 new MCRObjectID(id);
413 }
414
415 /**
416 * This method check this data again the input and retuns the result as
417 * boolean.
418 *
419 * @param in
420 * the MCRObjectID to check
421 * @return true if all parts are equal, else return false.
422 */
423 public boolean equals(MCRObjectID in) {
424 return (mcr_project_id.equals(in.mcr_project_id) && (mcr_type_id.equals(in.mcr_type_id)) && (mcr_number == in.mcr_number));
425 }
426
427 /**
428 * This method check this data again the input and retuns the result as
429 * boolean.
430 *
431 * @param in
432 * the MCRObjectID to check
433 * @return true if all parts are equal, else return false.
434 * @see java.lang.Object#equals(Object)
435 */
436 public boolean equals(Object in) {
437 if (!(in instanceof MCRObjectID)) {
438 return false;
439 }
440 return equals((MCRObjectID) in);
441 }
442
443 /**
444 * @see #getId()
445 * @see java.lang.Object#toString()
446 */
447 public String toString() {
448 return getId();
449 }
450
451 /**
452 * returns getId().hashCode()
453 *
454 * @see #getId()
455 * @see java.lang.Object#hashCode()
456 */
457 public int hashCode() {
458 return getId().hashCode();
459 }
460 }