cdmlib-commons/pom.xml -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/AccountStore.java -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/CdmUtils.java -text
+cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/DOI.java -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/DocUtils.java -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/DoubleResult.java -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/ExcelUtils.java -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/TreeNode.java -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/UTF8.java -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/UriUtils.java -text
+cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/UrlUtf8Coder.java -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/XmlHelp.java -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/media/AudioInfo.java -text
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/media/ImageInfo.java -text
cdmlib-commons/src/main/resources/MUST-EXIST.txt -text
cdmlib-commons/src/main/resources/log4j.properties -text
cdmlib-commons/src/test/java/eu/etaxonomy/cdm/common/CdmUtilsTest.java -text
+cdmlib-commons/src/test/java/eu/etaxonomy/cdm/common/DoiTest.java -text
cdmlib-commons/src/test/java/eu/etaxonomy/cdm/common/GeneralParserTest.java -text
cdmlib-commons/src/test/java/eu/etaxonomy/cdm/common/UriUtilsTest.java -text
cdmlib-commons/src/test/java/eu/etaxonomy/cdm/common/UuidGenerator.java -text
cdmlib-model/README.TXT -text
cdmlib-model/pom.xml -text
cdmlib-model/src/main/java/eu/etaxonomy/cdm/aspectj/PropertyChangeAspect.aj -text
+cdmlib-model/src/main/java/eu/etaxonomy/cdm/hibernate/DOIUserType.java -text
cdmlib-model/src/main/java/eu/etaxonomy/cdm/hibernate/EnumUserType.java -text
cdmlib-model/src/main/java/eu/etaxonomy/cdm/hibernate/HibernateProxyHelper.java -text
cdmlib-model/src/main/java/eu/etaxonomy/cdm/hibernate/PartialUserType.java -text
--- /dev/null
+/**\r
+* Copyright (C) 2007 EDIT\r
+* European Distributed Institute of Taxonomy\r
+* http://www.e-taxonomy.eu\r
+*\r
+* The contents of this file are subject to the Mozilla Public License Version 1.1\r
+* See LICENSE.TXT at the top of this package for the full license terms.\r
+*/\r
+package eu.etaxonomy.cdm.common;\r
+\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import org.apache.commons.lang.StringUtils;\r
+\r
+\r
+/**\r
+ * A class for handling DOIs (http://www.doi.org).\r
+ * It offers parsing and formatting functionality as well as validation.\r
+ * A {@link DOI} object can only be created by syntactic valid input.\r
+ * It internally stores a doi 2 strings, the first one being the registrant number\r
+ * (including sub numbers), the second being the suffix.\r
+ * \r
+ * \r
+ * @author a.mueller\r
+ * @created 2013-09-04\r
+ */\r
+public final class DOI implements java.io.Serializable{\r
+ \r
+ /**\r
+ * Explicit serialVersionUID for interoperability.\r
+ */\r
+ private static final long serialVersionUID = -3871039785359980553L;\r
+\r
+\r
+ /**\r
+ * The default public DOI proxy server\r
+ */\r
+ public static final String HTTP_DOI_ORG = "http://doi.org/";\r
+\r
+ /**\r
+ * The former default public DOI proxy server, still supported but no longer preferred.\r
+ * @see #HTTP_DOI_ORG\r
+ */\r
+ public static final String HTTP_OLD_DOI_ORG = "http://dx.doi.org/";\r
+ \r
+ private volatile transient int hashCode = -1; // Zero ==> undefined\r
+\r
+ //http://www.doi.org/doi_handbook/2_Numbering.html#2.2.1\r
+// prefix + suffix, no defined length, case-insensitive, any printable characters\r
+\r
+ \r
+//********************************* VARIABLES *************************************/ \r
+ \r
+ /**\r
+ * The directory indicator for DOIs as registered at \r
+ */\r
+ public static final String DIRECTORY_INDICATOR = "10";\r
+ private String prefix_registrantCode;\r
+\r
+ private String suffix;\r
+\r
+// ***************************** FACTORY METHODS ***************************************/\r
+ \r
+ public static DOI fromString(String doi) throws IllegalArgumentException{\r
+ return new DOI(doi);\r
+ }\r
+ \r
+ public static DOI fromRegistrantCodeAndSuffix(String registrantCode, String suffix) throws IllegalArgumentException{\r
+ return new DOI(registrantCode, suffix);\r
+ }\r
+ \r
+ \r
+// ******************************* CONSTRUCTOR ************************************/ \r
+ \r
+ /**\r
+ * Creates a doi by its registrantCode and its suffix\r
+ * @param registrantCode the registrant code, the is the part following the directoryIndicator "10." \r
+ * and preceding the first forward slash (followed by the suffix)\r
+ * @param suffix the suffix is the part of the DOI following the first forward slash. It is provided \r
+ * by the registrant\r
+ */\r
+ private DOI(String registrantCode, String suffix) {\r
+ //preliminary until prefix_registrantCode and suffix validation is implemented\r
+ this("10." + registrantCode + "/" + suffix);\r
+ \r
+ //use only after validation of both parts\r
+// this.prefix_registrantCode = registrantCode;\r
+// this.suffix = suffix;\r
+ }\r
+\r
+ private DOI(String doiString) {\r
+ super();\r
+ parseDoiString(doiString);\r
+ }\r
+\r
+//************************************ GETTER ***********************************/ \r
+ \r
+ public String getPrefix() {\r
+ return makePrefix();\r
+ }\r
+ \r
+ public String getPrefix_registrantCode() {\r
+ return prefix_registrantCode;\r
+ }\r
+\r
+ public String getSuffix() {\r
+ return suffix;\r
+ }\r
+\r
+ private static Pattern doiPattern = Pattern.compile("^doi:\\s*", Pattern.CASE_INSENSITIVE); \r
+ \r
+// ********************************************* PARSER *******************************/\r
+ \r
+ private void parseDoiString(String doi){\r
+ boolean isUrn = false;\r
+ if (StringUtils.isBlank(doi)){\r
+ throw new IllegalArgumentException("Doi string must not be null or blank");\r
+ }\r
+ doi = doi.trim();\r
+ if (doi.startsWith("https") ){\r
+ doi = doi.replaceFirst("https", "http").trim();\r
+ }\r
+ Matcher matcher = doiPattern.matcher(doi);\r
+ if (matcher.find()){\r
+ doi = matcher.replaceFirst("").trim();\r
+ }\r
+\r
+ \r
+ //replace URI prefix\r
+ if (doi.startsWith(HTTP_DOI_ORG)){\r
+ doi = doi.replaceFirst(HTTP_DOI_ORG,"");\r
+ }else if (doi.startsWith(HTTP_OLD_DOI_ORG)){\r
+ doi = doi.replaceFirst(HTTP_OLD_DOI_ORG,"");\r
+ }\r
+ \r
+ \r
+\r
+ //handle URN prefix\r
+ if (doi.startsWith("urn:doi:")){\r
+ doi = doi.replaceFirst("urn:doi:","");\r
+ isUrn = true;\r
+ }\r
+ \r
+ \r
+ //now we should have the pure doi\r
+ if (doi.length() > 1000){\r
+ //for persistence reason we currently restrict the length of DOIs to 1000\r
+ throw new IllegalArgumentException("DOIs may have a maximum length of 1000 in the CDM.");\r
+ }\r
+ \r
+ if (! doi.startsWith("10.")){\r
+ throw new IllegalArgumentException("DOI not parsable. DOI must start with 10. or an URI or URN prefix ");\r
+ }\r
+ doi = doi.substring(3);\r
+ String sep = isUrn? ":" : "/";\r
+ \r
+// registrant\r
+ String registrant = doi.split(sep)[0];\r
+ if (!registrant.matches("[0-9]{2,}(?:[.][0-9]+)*")){ //per definition the number of digits may also be 1, however the lowest known number is 3 so we may be on the safe side here \r
+ String message = "Invalid prefix '10.%s'";\r
+ throw new IllegalArgumentException(String.format(message, registrant));\r
+ }\r
+ //suffix\r
+ String suffix = doi.replaceFirst(registrant + sep,"");\r
+ if (! suffix.matches("\\p{Print}+")){\r
+ String message = "Suffix should only include printable characters";\r
+ throw new IllegalArgumentException(message);\r
+ }\r
+ if (isUrn){\r
+ //TODO do some other replacements according to http://www.doi.org/doi_handbook/2_Numbering.html#2.6.3\r
+ //e.g. slash becomes : in URN\r
+ //TODO do we need this also for other URIs? According to http://www.doi.org/doi_handbook/2_Numbering.html#2.6 it is only required for URNs\r
+ suffix = UrlUtf8Coder.unescape(suffix);\r
+ }\r
+ //success\r
+ this.prefix_registrantCode = registrant;\r
+ this.suffix = suffix;\r
+ \r
+ }\r
+ \r
+ \r
+ private String makePrefix(){\r
+ return DIRECTORY_INDICATOR + "." + this.prefix_registrantCode;\r
+ }\r
+ \r
+ private String makeDoi(){\r
+ return makePrefix() + "/" + this.suffix;\r
+ }\r
+ \r
+ public String asURI(){\r
+ return HTTP_DOI_ORG + makePrefix() + "/" + uriEncodedSuffix();\r
+ }\r
+ \r
+ private String uriEncodedSuffix() {\r
+ String result = UrlUtf8Coder.encode(this.suffix);\r
+ return result;\r
+ }\r
+\r
+//************************************************* toString/equals /hashCode *********************/ \r
+\r
+ \r
+ \r
+ @Override\r
+ public int hashCode() {\r
+ if (hashCode == -1) {\r
+ hashCode = 31 * prefix_registrantCode.toUpperCase().hashCode() + suffix.toUpperCase().hashCode();\r
+ }\r
+ return hashCode;\r
+ }\r
+\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (obj instanceof DOI){\r
+ DOI doi = (DOI)obj;\r
+ if (this.prefix_registrantCode.toUpperCase().equals(doi.prefix_registrantCode.toUpperCase()) &&\r
+ this.suffix.toUpperCase().equals(doi.suffix.toUpperCase())){\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+\r
+ @Override\r
+ public String toString(){\r
+ return makeDoi();\r
+ }\r
+}\r
--- /dev/null
+/**\r
+ * Provides a method to encode any string into a URL-safe\r
+ * form.\r
+ * Non-ASCII characters are first encoded as sequences of\r
+ * two or three bytes, using the UTF-8 algorithm, before being\r
+ * encoded as %HH escapes.\r
+ *\r
+ * Created: 17 April 1997\r
+ * Author: Bert Bos <bert@w3.org>\r
+ *\r
+ * URLUTF8Encoder: http://www.w3.org/International/URLUTF8Encoder.java\r
+ *\r
+ * Copyright © 1997 World Wide Web Consortium, (Massachusetts\r
+ * Institute of Technology, European Research Consortium for\r
+ * Informatics and Mathematics, Keio University). All Rights Reserved. \r
+ * This work is distributed under the W3C® Software License [1] in the\r
+ * hope that it will be useful, but WITHOUT ANY WARRANTY; without even\r
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
+ * PURPOSE.\r
+ *\r
+ * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231\r
+ */\r
+package eu.etaxonomy.cdm.common;\r
+\r
+public class UrlUtf8Coder{\r
+\r
+ final static String[] hex = {\r
+ "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",\r
+ "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",\r
+ "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",\r
+ "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",\r
+ "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",\r
+ "%28", "%29", "%2a", "%2b", "%2c", "%2d", "%2e", "%2f",\r
+ "%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37",\r
+ "%38", "%39", "%3a", "%3b", "%3c", "%3d", "%3e", "%3f",\r
+ "%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47",\r
+ "%48", "%49", "%4a", "%4b", "%4c", "%4d", "%4e", "%4f",\r
+ "%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57",\r
+ "%58", "%59", "%5a", "%5b", "%5c", "%5d", "%5e", "%5f",\r
+ "%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67",\r
+ "%68", "%69", "%6a", "%6b", "%6c", "%6d", "%6e", "%6f",\r
+ "%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77",\r
+ "%78", "%79", "%7a", "%7b", "%7c", "%7d", "%7e", "%7f",\r
+ "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",\r
+ "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",\r
+ "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",\r
+ "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",\r
+ "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",\r
+ "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",\r
+ "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",\r
+ "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",\r
+ "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",\r
+ "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",\r
+ "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",\r
+ "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",\r
+ "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",\r
+ "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",\r
+ "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",\r
+ "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"\r
+ };\r
+\r
+ /**\r
+ * Encode a string to the "x-www-form-urlencoded" form, enhanced\r
+ * with the UTF-8-in-URL proposal. This is what happens:\r
+ *\r
+ * <ul>\r
+ * <li><p>The ASCII characters 'a' through 'z', 'A' through 'Z',\r
+ * and '0' through '9' remain the same.\r
+ *\r
+ * <li><p>The unreserved characters - _ . ! ~ * ' ( ) remain the same.\r
+ *\r
+ * <li><p>The space character ' ' is converted into a plus sign '+'.\r
+ *\r
+ * <li><p>All other ASCII characters are converted into the\r
+ * 3-character string "%xy", where xy is\r
+ * the two-digit hexadecimal representation of the character\r
+ * code\r
+ *\r
+ * <li><p>All non-ASCII characters are encoded in two steps: first\r
+ * to a sequence of 2 or 3 bytes, using the UTF-8 algorithm;\r
+ * secondly each of these bytes is encoded as "%xx".\r
+ * </ul>\r
+ *\r
+ * @param s The string to be encoded\r
+ * @return The encoded string\r
+ */\r
+ public static String encode(String s)\r
+ {\r
+ StringBuffer sbuf = new StringBuffer();\r
+ int len = s.length();\r
+ for (int i = 0; i < len; i++) {\r
+ int ch = s.charAt(i);\r
+ if ('A' <= ch && ch <= 'Z') { // 'A'..'Z'\r
+ sbuf.append((char)ch);\r
+ } else if ('a' <= ch && ch <= 'z') { // 'a'..'z'\r
+ sbuf.append((char)ch);\r
+ } else if ('0' <= ch && ch <= '9') { // '0'..'9'\r
+ sbuf.append((char)ch);\r
+ } else if (ch == ' ') { // space\r
+ sbuf.append(hex[ch]); //Note: changed from + to %20 for use according to http://www.doi.org/doi_handbook/2_Numbering.html#2.5.2.4\r
+ } else if (ch == '-' || ch == '_' // unreserved\r
+ || ch == '.' || ch == '!'\r
+ || ch == '~' || ch == '*'\r
+ || ch == '\'' || ch == '('\r
+ || ch == ')') {\r
+ sbuf.append((char)ch);\r
+ } else if (ch <= 0x007f) { // other ASCII\r
+ sbuf.append(hex[ch]);\r
+ } else if (ch <= 0x07FF) { // non-ASCII <= 0x7FF\r
+ sbuf.append(hex[0xc0 | (ch >> 6)]);\r
+ sbuf.append(hex[0x80 | (ch & 0x3F)]);\r
+ } else { // 0x7FF < ch <= 0xFFFF\r
+ sbuf.append(hex[0xe0 | (ch >> 12)]);\r
+ sbuf.append(hex[0x80 | ((ch >> 6) & 0x3F)]);\r
+ sbuf.append(hex[0x80 | (ch & 0x3F)]);\r
+ }\r
+ }\r
+ return sbuf.toString();\r
+ }\r
+ \r
+ /*\r
+ * Created: 17 April 1997\r
+ * Author: Bert Bos <bert@w3.org>\r
+ *\r
+ * unescape: http://www.w3.org/International/unescape.java\r
+ *\r
+ * Copyright © 1997 World Wide Web Consortium, (Massachusetts\r
+ * Institute of Technology, European Research Consortium for\r
+ * Informatics and Mathematics, Keio University). All Rights Reserved. \r
+ * This work is distributed under the W3C® Software License [1] in the\r
+ * hope that it will be useful, but WITHOUT ANY WARRANTY; without even\r
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
+ * PURPOSE.\r
+ *\r
+ * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231\r
+ */\r
+ public static String unescape(String s) {\r
+ StringBuffer sbuf = new StringBuffer () ;\r
+ int l = s.length() ;\r
+ int ch = -1 ;\r
+ int b, sumb = 0;\r
+ for (int i = 0, more = -1 ; i < l ; i++) {\r
+ /* Get next byte b from URL segment s */\r
+ switch (ch = s.charAt(i)) {\r
+ case '%':\r
+ ch = s.charAt (++i) ;\r
+ int hb = (Character.isDigit ((char) ch) \r
+ ? ch - '0'\r
+ : 10+Character.toLowerCase((char) ch) - 'a') & 0xF ;\r
+ ch = s.charAt (++i) ;\r
+ int lb = (Character.isDigit ((char) ch)\r
+ ? ch - '0'\r
+ : 10+Character.toLowerCase ((char) ch)-'a') & 0xF ;\r
+ b = (hb << 4) | lb ;\r
+ break ;\r
+ case '+':\r
+ b = ' ' ;\r
+ break ;\r
+ default:\r
+ b = ch ;\r
+ }\r
+ /* Decode byte b as UTF-8, sumb collects incomplete chars */\r
+ if ((b & 0xc0) == 0x80) { // 10xxxxxx (continuation byte)\r
+ sumb = (sumb << 6) | (b & 0x3f) ; // Add 6 bits to sumb\r
+ if (--more == 0) sbuf.append((char) sumb) ; // Add char to sbuf\r
+ } else if ((b & 0x80) == 0x00) { // 0xxxxxxx (yields 7 bits)\r
+ sbuf.append((char) b) ; // Store in sbuf\r
+ } else if ((b & 0xe0) == 0xc0) { // 110xxxxx (yields 5 bits)\r
+ sumb = b & 0x1f;\r
+ more = 1; // Expect 1 more byte\r
+ } else if ((b & 0xf0) == 0xe0) { // 1110xxxx (yields 4 bits)\r
+ sumb = b & 0x0f;\r
+ more = 2; // Expect 2 more bytes\r
+ } else if ((b & 0xf8) == 0xf0) { // 11110xxx (yields 3 bits)\r
+ sumb = b & 0x07;\r
+ more = 3; // Expect 3 more bytes\r
+ } else if ((b & 0xfc) == 0xf8) { // 111110xx (yields 2 bits)\r
+ sumb = b & 0x03;\r
+ more = 4; // Expect 4 more bytes\r
+ } else /*if ((b & 0xfe) == 0xfc)*/ { // 1111110x (yields 1 bit)\r
+ sumb = b & 0x01;\r
+ more = 5; // Expect 5 more bytes\r
+ }\r
+ /* We don't test if the UTF-8 encoding is well-formed */\r
+ }\r
+ return sbuf.toString() ;\r
+ }\r
+\r
+}\r
--- /dev/null
+/**\r
+* Copyright (C) 2007 EDIT\r
+* European Distributed Institute of Taxonomy\r
+* http://www.e-taxonomy.eu\r
+*\r
+* The contents of this file are subject to the Mozilla Public License Version 1.1\r
+* See LICENSE.TXT at the top of this package for the full license terms.\r
+*/\r
+package eu.etaxonomy.cdm.common;\r
+\r
+import org.junit.Assert;\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+/**\r
+ * \r
+ * Test class for testing the {@link DOI} class.\r
+ * \r
+ * For doi syntax see also http://www.doi.org/doi_handbook/2_Numbering.html\r
+ * or\r
+ * http://stackoverflow.com/questions/27910/finding-a-doi-in-a-document-or-page \r
+ * \r
+ * @author a.mueller\r
+ *\r
+ */\r
+public class DoiTest {\r
+\r
+ /**\r
+ * @throws java.lang.Exception\r
+ */\r
+ @Before\r
+ public void setUp() throws Exception {\r
+ }\r
+\r
+ @Test\r
+ public void testValidParser() {\r
+ String validDoi = "10.1002/1234";\r
+ DOI doi = DOI.fromString(validDoi);\r
+ Assert.assertEquals("10.1002", doi.getPrefix());\r
+ Assert.assertEquals("1234", doi.getSuffix());\r
+ \r
+ validDoi = "10.1002/(SICI)1522-2594(199911)42:5<952::AID-MRM16>3.0.CO;2-S";\r
+ doi = DOI.fromString(validDoi);\r
+ Assert.assertEquals("10.1002", doi.getPrefix());\r
+ Assert.assertEquals("(SICI)1522-2594(199911)42:5<952::AID-MRM16>3.0.CO;2-S", doi.getSuffix());\r
+ \r
+ validDoi = "10.1007.10/978-3-642-28108-2_19";\r
+ doi = DOI.fromString(validDoi);\r
+ Assert.assertEquals("10.1007.10", doi.getPrefix());\r
+ Assert.assertEquals("978-3-642-28108-2_19", doi.getSuffix());\r
+ \r
+ validDoi="10.1579/0044-7447(2006)35\\[89:RDUICP\\]2.0.CO;2";\r
+ doi = DOI.fromString(validDoi);\r
+ Assert.assertEquals("10.1579", doi.getPrefix());\r
+ Assert.assertEquals("0044-7447(2006)35\\[89:RDUICP\\]2.0.CO;2", doi.getSuffix());\r
+\r
+ }\r
+ \r
+ @Test\r
+ public void testFromRegistrantCodeAndSuffix() {\r
+ DOI doi = DOI.fromRegistrantCodeAndSuffix("1579", "978-3-642-28108-2_19");\r
+ Assert.assertEquals("10.1579", doi.getPrefix());\r
+ Assert.assertEquals("978-3-642-28108-2_19", doi.getSuffix());\r
+ Assert.assertNotEquals("1234", doi.getSuffix());\r
+ }\r
+ \r
+ @Test\r
+ public void testParserFail() {\r
+ String invalidDoi = "10.4515260,51.1656910"; //must never match to avoid matches with geo coordinates\r
+ testInvalid(invalidDoi);\r
+ invalidDoi = "4210.1000/123456"; //directoryIndicator must always be 10\r
+ testInvalid(invalidDoi);\r
+ invalidDoi = "10.1002/12\u0004345"; //control characters (here U+0004) must fail\r
+ testInvalid(invalidDoi);\r
+ invalidDoi = "10.1a02/12345"; //registrant code must include only number and dots (to separate sub codes)\r
+ testInvalid(invalidDoi);\r
+ invalidDoi = "10.1002:12345"; //column separator is only allowed (+required) in URNs\r
+ testInvalid(invalidDoi);\r
+ invalidDoi = "10.1002/"; //doi must always have a suffix length > 0 (if this should changed in future, please do adapt equals and hashCode)\r
+ testInvalid(invalidDoi);\r
+ invalidDoi = "10./1234"; //doi must always have a registrant prefix length > 0 (if this should changed in future, please do adapt equals and hashCode)\r
+ testInvalid(invalidDoi);\r
+ }\r
+\r
+ @Test\r
+ public void testParserWithPrefixes() {\r
+ String validDoi = "DOI: 10.1002/1234";\r
+ DOI doi = DOI.fromString(validDoi);\r
+ Assert.assertEquals("10.1002", doi.getPrefix());\r
+ Assert.assertEquals("1234", doi.getSuffix());\r
+ \r
+ validDoi = "http://doi.org/10.1002/1234";\r
+ doi = DOI.fromString(validDoi);\r
+ Assert.assertEquals("10.1002", doi.getPrefix());\r
+ Assert.assertEquals("1234", doi.getSuffix());\r
+ \r
+ \r
+ validDoi = "http://doi.org/urn:doi:10.123:456ABC%2Fzyz";\r
+ doi = DOI.fromString(validDoi);\r
+ Assert.assertEquals("10.123", doi.getPrefix());\r
+ Assert.assertEquals("456ABC/zyz", doi.getSuffix()); //urn must be percentage encoded ( / -> %2F)\r
+ \r
+ }\r
+ \r
+ @Test\r
+ public void testEquals() {\r
+ String validDoi = "10.1002/12a4";\r
+ DOI doi1 = DOI.fromString(validDoi);\r
+ validDoi = "10.1002/12A4";\r
+ DOI doi2 = DOI.fromString(validDoi);\r
+ Assert.assertEquals("DOIs must be equal case insensitive", doi1, doi2);\r
+ validDoi = "10.1002/12b4";\r
+ DOI doi3 = DOI.fromString(validDoi);\r
+ Assert.assertNotEquals("Different DOIs must not be equal", doi1, doi3);\r
+ }\r
+ \r
+ @Test\r
+ public void testAsURI() {\r
+ //mandatory encoding according to http://www.doi.org/doi_handbook/2_Numbering.html#2.5.2.4\r
+ String validDoi = "10.1002/1234%56\"78#90 12?34";\r
+ DOI doi1 = DOI.fromString(validDoi);\r
+ String uri = doi1.asURI();\r
+ Assert.assertEquals(DOI.HTTP_DOI_ORG + "10.1002/1234%2556%2278%2390%2012%3f34", uri);\r
+ \r
+ //recommendedEncoding\r
+ validDoi = "10.1002/1234<56>78{90}12^34";\r
+ doi1 = DOI.fromString(validDoi);\r
+ uri = doi1.asURI();\r
+ Assert.assertEquals(DOI.HTTP_DOI_ORG + "10.1002/1234%3c56%3e78%7b90%7d12%5e34", uri);\r
+ \r
+ //recommendedEncoding (cont.)\r
+ validDoi = "10.1002/1234[56]78`90|12\\34+56";\r
+ doi1 = DOI.fromString(validDoi);\r
+ uri = doi1.asURI();\r
+ Assert.assertEquals(DOI.HTTP_DOI_ORG + "10.1002/1234%5b56%5d78%6090%7c12%5c34%2b56", uri);\r
+ \r
+ }\r
+\r
+ \r
+\r
+ \r
+ private void testInvalid(String invalidDoi) {\r
+ try {\r
+ DOI.fromString(invalidDoi);\r
+ Assert.fail("DOI should not be parsable: " + invalidDoi);\r
+ } catch (IllegalArgumentException e) {\r
+ //OK\r
+ }\r
+ } \r
+\r
+}\r
--- /dev/null
+/**\r
+* Copyright (C) 2007 EDIT\r
+* European Distributed Institute of Taxonomy \r
+* http://www.e-taxonomy.eu\r
+* \r
+* The contents of this file are subject to the Mozilla Public License Version 1.1\r
+* See LICENSE.TXT at the top of this package for the full license terms.\r
+*/\r
+\r
+package eu.etaxonomy.cdm.hibernate;\r
+\r
+import java.io.Serializable;\r
+import java.sql.PreparedStatement;\r
+import java.sql.ResultSet;\r
+import java.sql.SQLException;\r
+import java.sql.Types;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.hibernate.HibernateException;\r
+import org.hibernate.engine.spi.SessionImplementor;\r
+import org.hibernate.type.StandardBasicTypes;\r
+import org.hibernate.usertype.UserType;\r
+import org.jadira.usertype.dateandtime.shared.spi.AbstractUserType;\r
+\r
+import eu.etaxonomy.cdm.common.DOI;\r
+\r
+/**\r
+ * Hibernate user type for the {@link DOI} class.\r
+ * @author a.mueller\r
+ * @created 05.09.2013\r
+ */\r
+public class DOIUserType extends AbstractUserType implements UserType {\r
+ private static final long serialVersionUID = 2227841000128722278L;\r
+\r
+ @SuppressWarnings("unused")\r
+ private static final Logger logger = Logger.getLogger(DOIUserType.class);\r
+\r
+ private static final int[] SQL_TYPES = { Types.VARCHAR };\r
+\r
+ @Override\r
+ public Object deepCopy(Object o) throws HibernateException {\r
+ if (o == null) {\r
+ return null;\r
+ }\r
+ \r
+ DOI doi = (DOI) o;\r
+\r
+ try {\r
+ return DOI.fromString(doi.toString());\r
+ } catch (IllegalArgumentException e) {\r
+ throw new HibernateException(e);\r
+ }\r
+ }\r
+\r
+\r
+ @Override\r
+ public Serializable disassemble(Object value) throws HibernateException {\r
+ if(value == null) {\r
+ return null;\r
+ } else {\r
+ DOI doi = (DOI) value;\r
+ return doi.toString();\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public DOI nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) \r
+ throws HibernateException, SQLException {\r
+ String val = (String) StandardBasicTypes.STRING.nullSafeGet(rs, names, session, owner);\r
+ \r
+ if(val == null) {\r
+ return null;\r
+ } else {\r
+\r
+ try {\r
+ return DOI.fromString(val);\r
+ } catch (IllegalArgumentException e) {\r
+ throw new HibernateException(e);\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor session) \r
+ throws HibernateException, SQLException {\r
+ if (value == null) { \r
+ StandardBasicTypes.STRING.nullSafeSet(statement, value, index, session);\r
+ } else { \r
+ DOI doi = (DOI)value;\r
+ StandardBasicTypes.STRING.nullSafeSet(statement, doi.toString(), index, session);\r
+ }\r
+ }\r
+\r
+\r
+ /* (non-Javadoc)\r
+ * @see org.jadira.usertype.dateandtime.shared.spi.AbstractSingleColumnUserType#returnedClass()\r
+ */\r
+ @Override\r
+ public Class returnedClass() {\r
+ return DOI.class;\r
+ }\r
+\r
+ @Override\r
+ public int[] sqlTypes() {\r
+ return SQL_TYPES;\r
+ }\r
+\r
+ \r
+\r
+\r
+\r
+}\r
@org.hibernate.annotations.TypeDef(name="partialUserType", typeClass=eu.etaxonomy.cdm.hibernate.PartialUserType.class),\r
@org.hibernate.annotations.TypeDef(name="uuidUserType", typeClass=eu.etaxonomy.cdm.hibernate.UUIDUserType.class),\r
@org.hibernate.annotations.TypeDef(name="uriUserType", typeClass=eu.etaxonomy.cdm.hibernate.URIUserType.class),\r
- @org.hibernate.annotations.TypeDef(name="enumUserType", typeClass=eu.etaxonomy.cdm.hibernate.EnumUserType.class) \r
+ @org.hibernate.annotations.TypeDef(name="enumUserType", typeClass=eu.etaxonomy.cdm.hibernate.EnumUserType.class), \r
+ @org.hibernate.annotations.TypeDef(name="doiUserType", typeClass=eu.etaxonomy.cdm.hibernate.DOIUserType.class) \r
})\r
@org.hibernate.annotations.AnyMetaDef(name = "CdmBase" ,\r
metaType="string",\r
\r
package eu.etaxonomy.cdm.model.reference;\r
\r
+import eu.etaxonomy.cdm.common.DOI;\r
+\r
/**\r
* This base interface represents all different kind of published \r
* {@link IReference references} which constitute a physical \r
/**\r
* @return\r
*/\r
- public String getDoi();\r
+ public DOI getDoi();\r
\r
- public void setDoi(String doi);\r
+ public void setDoi(DOI doi);\r
\r
}\r
import org.hibernate.search.annotations.IndexedEmbedded;\r
import org.hibernate.validator.constraints.Length;\r
\r
+import eu.etaxonomy.cdm.common.DOI;\r
import eu.etaxonomy.cdm.model.agent.Institution;\r
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;\r
import eu.etaxonomy.cdm.model.common.TimePeriod;\r
\r
@XmlElement(name = "Doi")\r
@Field\r
- @NullOrNotEmpty\r
- @Length(max = 255)\r
-// @Pattern(regexp = "(?=.{13}$)\\d{1,5}([- ])\\d{1,7}\\1\\d{1,6}\\1(\\d|X)$", groups = Level2.class, message = "{eu.etaxonomy.cdm.model.reference.Reference.doi.message}")\r
- protected String doi;\r
+// @NullOrNotEmpty\r
+// @Length(max = 1000)\r
+ @Type(type="doiUserType")\r
+ protected DOI doi;\r
\r
\r
@XmlElement(name = "ISSN")\r
}\r
\r
@Override\r
- public String getDoi() {\r
+ public DOI getDoi() {\r
return doi;\r
}\r
\r
@Override\r
- public void setDoi(String doi) {\r
+ public void setDoi(DOI doi) {\r
this.doi = doi;\r
}\r
\r
import org.apache.log4j.Logger;\r
\r
import eu.etaxonomy.cdm.common.CdmUtils;\r
+import eu.etaxonomy.cdm.common.DOI;\r
import eu.etaxonomy.cdm.common.DoubleResult;\r
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
import eu.etaxonomy.cdm.model.common.CdmBase;\r
//result &= matchPrimitiveField(matchFirst, matchSecond, fieldMatcher, replaceModeList);\r
}else if(fieldType == URI.class){\r
result &= matchPrimitiveField(matchFirst, matchSecond, fieldMatcher, replaceModeList);\r
+ }else if(fieldType == DOI.class){\r
+ result &= matchPrimitiveField(matchFirst, matchSecond, fieldMatcher, replaceModeList);\r
}else if(isSingleCdmBaseObject(fieldType)){\r
result &= matchPrimitiveField(matchFirst, matchSecond, fieldMatcher, replaceModeList);\r
}else if (isCollection(fieldType)){\r