|
|
@@ -28,8 +28,6 @@ import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException; |
|
|
|
/** |
|
|
|
* An immutable Open Packaging Convention compliant part name. |
|
|
|
* |
|
|
|
* @author Julien Chable |
|
|
|
* |
|
|
|
* @see <a href="http://www.ietf.org/rfc/rfc3986.txt">http://www.ietf.org/rfc/rfc3986.txt</a> |
|
|
|
*/ |
|
|
|
public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
@@ -37,32 +35,31 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
/** |
|
|
|
* Part name stored as an URI. |
|
|
|
*/ |
|
|
|
private URI partNameURI; |
|
|
|
private final URI partNameURI; |
|
|
|
|
|
|
|
/* |
|
|
|
* URI Characters definition (RFC 3986) |
|
|
|
*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* Reserved characters for sub delimitations. |
|
|
|
* Reserved characters for sub delimiters. |
|
|
|
*/ |
|
|
|
private static String[] RFC3986_PCHAR_SUB_DELIMS = { "!", "$", "&", "'", |
|
|
|
"(", ")", "*", "+", ",", ";", "=" }; |
|
|
|
private static final String RFC3986_PCHAR_SUB_DELIMS = "!$&'()*+,;="; |
|
|
|
|
|
|
|
/** |
|
|
|
* Unreserved character (+ ALPHA & DIGIT). |
|
|
|
*/ |
|
|
|
private static String[] RFC3986_PCHAR_UNRESERVED_SUP = { "-", ".", "_", "~" }; |
|
|
|
private static final String RFC3986_PCHAR_UNRESERVED_SUP = "-._~"; |
|
|
|
|
|
|
|
/** |
|
|
|
* Authorized reserved characters for pChar. |
|
|
|
*/ |
|
|
|
private static String[] RFC3986_PCHAR_AUTHORIZED_SUP = { ":", "@" }; |
|
|
|
private static final String RFC3986_PCHAR_AUTHORIZED_SUP = ":@"; |
|
|
|
|
|
|
|
/** |
|
|
|
* Flag to know if this part name is from a relationship part name. |
|
|
|
*/ |
|
|
|
private boolean isRelationship; |
|
|
|
private final boolean isRelationship; |
|
|
|
|
|
|
|
/** |
|
|
|
* Constructor. Makes a ValidPartName object from a java.net.URI |
|
|
@@ -70,7 +67,7 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
* @param uri |
|
|
|
* The URI to validate and to transform into ValidPartName. |
|
|
|
* @param checkConformance |
|
|
|
* Flag to specify if the contructor have to validate the OPC |
|
|
|
* Flag to specify if the constructor have to validate the OPC |
|
|
|
* conformance. Must be always <code>true</code> except for |
|
|
|
* special URI like '/' which is needed for internal use by |
|
|
|
* OpenXML4J but is not valid. |
|
|
@@ -99,7 +96,7 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
* @param partName |
|
|
|
* Part name to valid and to create. |
|
|
|
* @param checkConformance |
|
|
|
* Flag to specify if the contructor have to validate the OPC |
|
|
|
* Flag to specify if the constructor have to validate the OPC |
|
|
|
* conformance. Must be always <code>true</code> except for |
|
|
|
* special URI like '/' which is needed for internal use by |
|
|
|
* OpenXML4J but is not valid. |
|
|
@@ -138,8 +135,9 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
* part naming convention else <code>false</code>. |
|
|
|
*/ |
|
|
|
private boolean isRelationshipPartURI(URI partUri) { |
|
|
|
if (partUri == null) |
|
|
|
throw new IllegalArgumentException("partUri"); |
|
|
|
if (partUri == null) { |
|
|
|
throw new IllegalArgumentException("partUri"); |
|
|
|
} |
|
|
|
|
|
|
|
return partUri.getPath().matches( |
|
|
|
"^.*/" + PackagingURIHelper.RELATIONSHIP_PART_SEGMENT_NAME + "/.*\\" |
|
|
@@ -168,8 +166,9 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
*/ |
|
|
|
private static void throwExceptionIfInvalidPartUri(URI partUri) |
|
|
|
throws InvalidFormatException { |
|
|
|
if (partUri == null) |
|
|
|
throw new IllegalArgumentException("partUri"); |
|
|
|
if (partUri == null) { |
|
|
|
throw new IllegalArgumentException("partUri"); |
|
|
|
} |
|
|
|
// Check if the part name URI is empty [M1.1] |
|
|
|
throwExceptionIfEmptyURI(partUri); |
|
|
|
|
|
|
@@ -197,15 +196,17 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
*/ |
|
|
|
private static void throwExceptionIfEmptyURI(URI partURI) |
|
|
|
throws InvalidFormatException { |
|
|
|
if (partURI == null) |
|
|
|
throw new IllegalArgumentException("partURI"); |
|
|
|
if (partURI == null) { |
|
|
|
throw new IllegalArgumentException("partURI"); |
|
|
|
} |
|
|
|
|
|
|
|
String uriPath = partURI.getPath(); |
|
|
|
if (uriPath.length() == 0 |
|
|
|
|| ((uriPath.length() == 1) && (uriPath.charAt(0) == PackagingURIHelper.FORWARD_SLASH_CHAR))) |
|
|
|
throw new InvalidFormatException( |
|
|
|
|| ((uriPath.length() == 1) && (uriPath.charAt(0) == PackagingURIHelper.FORWARD_SLASH_CHAR))) { |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A part name shall not be empty [M1.1]: " |
|
|
|
+ partURI.getPath()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@@ -240,32 +241,31 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
} |
|
|
|
|
|
|
|
// Split the URI into several part and analyze each |
|
|
|
String[] segments = partUri.toASCIIString().split("/"); |
|
|
|
if (segments.length <= 1 || !segments[0].isEmpty()) |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A part name shall not have empty segments [M1.3]: " |
|
|
|
+ partUri.getPath()); |
|
|
|
String[] segments = partUri.toASCIIString() |
|
|
|
.replaceFirst("^"+PackagingURIHelper.FORWARD_SLASH_CHAR,"") |
|
|
|
.split(PackagingURIHelper.FORWARD_SLASH_STRING); |
|
|
|
|
|
|
|
if (segments.length < 1) { |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A part name shall not have empty segments [M1.3]: " + partUri.getPath()); |
|
|
|
} |
|
|
|
|
|
|
|
for (int i = 1; i < segments.length; ++i) { |
|
|
|
String seg = segments[i]; |
|
|
|
for (final String seg : segments) { |
|
|
|
if (seg == null || seg.isEmpty()) { |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A part name shall not have empty segments [M1.3]: " |
|
|
|
+ partUri.getPath()); |
|
|
|
"A part name shall not have empty segments [M1.3]: " + partUri.getPath()); |
|
|
|
} |
|
|
|
|
|
|
|
if (seg.endsWith(".")) { |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A segment shall not end with a dot ('.') character [M1.9]: " |
|
|
|
+ partUri.getPath()); |
|
|
|
"A segment shall not end with a dot ('.') character [M1.9]: " + partUri.getPath()); |
|
|
|
} |
|
|
|
|
|
|
|
if (seg.replaceAll("\\\\.", "").isEmpty()) { |
|
|
|
// Normally will never been invoked with the previous |
|
|
|
// implementation rule [M1.9] |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A segment shall include at least one non-dot character. [M1.10]: " |
|
|
|
+ partUri.getPath()); |
|
|
|
"A segment shall include at least one non-dot character. [M1.10]: " + partUri.getPath()); |
|
|
|
} |
|
|
|
|
|
|
|
// Check for rule M1.6, M1.7, M1.8 |
|
|
@@ -288,93 +288,60 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
*/ |
|
|
|
private static void checkPCharCompliance(String segment) |
|
|
|
throws InvalidFormatException { |
|
|
|
boolean errorFlag; |
|
|
|
final int length = segment.length(); |
|
|
|
for (int i = 0; i < length; ++i) { |
|
|
|
char c = segment.charAt(i); |
|
|
|
errorFlag = true; |
|
|
|
final char c = segment.charAt(i); |
|
|
|
|
|
|
|
/* Check rule M1.6 */ |
|
|
|
|
|
|
|
// Check for digit or letter |
|
|
|
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') |
|
|
|
|| (c >= '0' && c <= '9')) { |
|
|
|
errorFlag = false; |
|
|
|
} else { |
|
|
|
// Check "-", ".", "_", "~" |
|
|
|
for (int j = 0; j < RFC3986_PCHAR_UNRESERVED_SUP.length; ++j) { |
|
|
|
if (c == RFC3986_PCHAR_UNRESERVED_SUP[j].charAt(0)) { |
|
|
|
errorFlag = false; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Check ":", "@" |
|
|
|
for (int j = 0; errorFlag |
|
|
|
&& j < RFC3986_PCHAR_AUTHORIZED_SUP.length; ++j) { |
|
|
|
if (c == RFC3986_PCHAR_AUTHORIZED_SUP[j].charAt(0)) { |
|
|
|
errorFlag = false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Check "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" |
|
|
|
for (int j = 0; errorFlag |
|
|
|
&& j < RFC3986_PCHAR_SUB_DELIMS.length; ++j) { |
|
|
|
if (c == RFC3986_PCHAR_SUB_DELIMS[j].charAt(0)) { |
|
|
|
errorFlag = false; |
|
|
|
} |
|
|
|
} |
|
|
|
if ( |
|
|
|
// Check for digit or letter |
|
|
|
isDigitOrLetter(c) || |
|
|
|
// Check "-", ".", "_", "~" |
|
|
|
RFC3986_PCHAR_UNRESERVED_SUP.indexOf(c) > -1 || |
|
|
|
// Check ":", "@" |
|
|
|
RFC3986_PCHAR_AUTHORIZED_SUP.indexOf(c) > -1 || |
|
|
|
// Check "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" |
|
|
|
RFC3986_PCHAR_SUB_DELIMS.indexOf(c) > -1 |
|
|
|
) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
if (errorFlag && c == '%') { |
|
|
|
// We certainly found an encoded character, check for length |
|
|
|
// now ( '%' HEXDIGIT HEXDIGIT) |
|
|
|
if (((length - i) < 2)) { |
|
|
|
throw new InvalidFormatException("The segment " + segment |
|
|
|
+ " contain invalid encoded character !"); |
|
|
|
} |
|
|
|
|
|
|
|
// If not percent encoded character error occur then reset the |
|
|
|
// flag -> the character is valid |
|
|
|
errorFlag = false; |
|
|
|
|
|
|
|
// Decode the encoded character |
|
|
|
char decodedChar = (char) Integer.parseInt(segment.substring( |
|
|
|
i + 1, i + 3), 16); |
|
|
|
i += 2; |
|
|
|
|
|
|
|
/* Check rule M1.7 */ |
|
|
|
if (decodedChar == '/' || decodedChar == '\\') |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A segment shall not contain percent-encoded forward slash ('/'), or backward slash ('\') characters. [M1.7]"); |
|
|
|
|
|
|
|
/* Check rule M1.8 */ |
|
|
|
|
|
|
|
// Check for unreserved character like define in RFC3986 |
|
|
|
if ((decodedChar >= 'A' && decodedChar <= 'Z') |
|
|
|
|| (decodedChar >= 'a' && decodedChar <= 'z') |
|
|
|
|| (decodedChar >= '0' && decodedChar <= '9')) |
|
|
|
errorFlag = true; |
|
|
|
|
|
|
|
// Check for unreserved character "-", ".", "_", "~" |
|
|
|
for (int j = 0; !errorFlag |
|
|
|
&& j < RFC3986_PCHAR_UNRESERVED_SUP.length; ++j) { |
|
|
|
if (c == RFC3986_PCHAR_UNRESERVED_SUP[j].charAt(0)) { |
|
|
|
errorFlag = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
if (errorFlag) |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A segment shall not contain percent-encoded unreserved characters. [M1.8]"); |
|
|
|
|
|
|
|
|
|
|
|
if (c != '%') { |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A segment shall not hold any characters other than pchar characters. [M1.6]"); |
|
|
|
} |
|
|
|
|
|
|
|
// We certainly found an encoded character, check for length |
|
|
|
// now ( '%' HEXDIGIT HEXDIGIT) |
|
|
|
if ((length - i) < 2 || !isHexDigit(segment.charAt(i+1)) || !isHexDigit(segment.charAt(i+2))) { |
|
|
|
throw new InvalidFormatException("The segment " + segment + " contain invalid encoded character !"); |
|
|
|
} |
|
|
|
|
|
|
|
if (errorFlag) |
|
|
|
// Decode the encoded character |
|
|
|
final char decodedChar = (char) Integer.parseInt(segment.substring(i + 1, i + 3), 16); |
|
|
|
i += 2; |
|
|
|
|
|
|
|
/* Check rule M1.7 */ |
|
|
|
if (decodedChar == '/' || decodedChar == '\\') { |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A segment shall not hold any characters other than pchar characters. [M1.6]"); |
|
|
|
"A segment shall not contain percent-encoded forward slash ('/'), or backward slash ('\') characters. [M1.7]"); |
|
|
|
} |
|
|
|
|
|
|
|
/* Check rule M1.8 */ |
|
|
|
if ( |
|
|
|
// Check for unreserved character like define in RFC3986 |
|
|
|
isDigitOrLetter(decodedChar) || |
|
|
|
// Check for unreserved character "-", ".", "_", "~" |
|
|
|
RFC3986_PCHAR_UNRESERVED_SUP.indexOf(decodedChar) > -1 |
|
|
|
) { |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A segment shall not contain percent-encoded unreserved characters. [M1.8]"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Throws an exception if the specified part name doesn't start with a |
|
|
|
* forward slash character '/'. [M1.4] |
|
|
@@ -389,10 +356,11 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
URI partUri) throws InvalidFormatException { |
|
|
|
String uriPath = partUri.getPath(); |
|
|
|
if (uriPath.length() > 0 |
|
|
|
&& uriPath.charAt(0) != PackagingURIHelper.FORWARD_SLASH_CHAR) |
|
|
|
throw new InvalidFormatException( |
|
|
|
&& uriPath.charAt(0) != PackagingURIHelper.FORWARD_SLASH_CHAR) { |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A part name shall start with a forward slash ('/') character [M1.4]: " |
|
|
|
+ partUri.getPath()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@@ -409,10 +377,11 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
URI partUri) throws InvalidFormatException { |
|
|
|
String uriPath = partUri.getPath(); |
|
|
|
if (uriPath.length() > 0 |
|
|
|
&& uriPath.charAt(uriPath.length() - 1) == PackagingURIHelper.FORWARD_SLASH_CHAR) |
|
|
|
throw new InvalidFormatException( |
|
|
|
&& uriPath.charAt(uriPath.length() - 1) == PackagingURIHelper.FORWARD_SLASH_CHAR) { |
|
|
|
throw new InvalidFormatException( |
|
|
|
"A part name shall not have a forward slash as the last character [M1.5]: " |
|
|
|
+ partUri.getPath()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@@ -423,11 +392,10 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
* @throws InvalidFormatException |
|
|
|
* Throws if the specified URI is absolute. |
|
|
|
*/ |
|
|
|
private static void throwExceptionIfAbsoluteUri(URI partUri) |
|
|
|
throws InvalidFormatException { |
|
|
|
if (partUri.isAbsolute()) |
|
|
|
throw new InvalidFormatException("Absolute URI forbidden: " |
|
|
|
+ partUri); |
|
|
|
private static void throwExceptionIfAbsoluteUri(URI partUri) throws InvalidFormatException { |
|
|
|
if (partUri.isAbsolute()) { |
|
|
|
throw new InvalidFormatException("Absolute URI forbidden: " + partUri); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@@ -438,12 +406,11 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
* part names and package implementers shall neither create nor recognize |
|
|
|
* packages with equivalent part names. [M1.12] |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
public int compareTo(PackagePartName other) |
|
|
|
{ |
|
|
|
// compare with natural sort order |
|
|
|
return compare(this, other); |
|
|
|
} |
|
|
|
@Override |
|
|
|
public int compareTo(PackagePartName other) { |
|
|
|
// compare with natural sort order |
|
|
|
return compare(this, other); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
@@ -456,8 +423,9 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
String fragment = this.partNameURI.getPath(); |
|
|
|
if (fragment.length() > 0) { |
|
|
|
int i = fragment.lastIndexOf("."); |
|
|
|
if (i > -1) |
|
|
|
return fragment.substring(i + 1); |
|
|
|
if (i > -1) { |
|
|
|
return fragment.substring(i + 1); |
|
|
|
} |
|
|
|
} |
|
|
|
return ""; |
|
|
|
} |
|
|
@@ -468,7 +436,7 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
* @return The name of this part name. |
|
|
|
*/ |
|
|
|
public String getName() { |
|
|
|
return this.partNameURI.toASCIIString(); |
|
|
|
return getURI().toASCIIString(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@@ -479,20 +447,13 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
public boolean equals(Object other) { |
|
|
|
if (other instanceof PackagePartName) { |
|
|
|
// String.equals() is compatible with our compareTo(), but cheaper |
|
|
|
return this.partNameURI.toASCIIString().toLowerCase(Locale.ROOT).equals |
|
|
|
( |
|
|
|
((PackagePartName) other).partNameURI.toASCIIString().toLowerCase(Locale.ROOT) |
|
|
|
); |
|
|
|
} else { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
return (other instanceof PackagePartName) && |
|
|
|
compare(this.getName(), ((PackagePartName)other).getName()) == 0; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public int hashCode() { |
|
|
|
return this.partNameURI.toASCIIString().toLowerCase(Locale.ROOT).hashCode(); |
|
|
|
return getName().toLowerCase(Locale.ROOT).hashCode(); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
@@ -529,24 +490,10 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
* part names and package implementers shall neither create nor recognize |
|
|
|
* packages with equivalent part names. [M1.12] |
|
|
|
*/ |
|
|
|
public static int compare(PackagePartName obj1, PackagePartName obj2) |
|
|
|
{ |
|
|
|
// NOTE could also throw a NullPointerException() if desired |
|
|
|
if (obj1 == null) |
|
|
|
{ |
|
|
|
// (null) == (null), (null) < (non-null) |
|
|
|
return (obj2 == null ? 0 : -1); |
|
|
|
} |
|
|
|
else if (obj2 == null) |
|
|
|
{ |
|
|
|
// (non-null) > (null) |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
return compare |
|
|
|
( |
|
|
|
obj1.getURI().toASCIIString().toLowerCase(Locale.ROOT), |
|
|
|
obj2.getURI().toASCIIString().toLowerCase(Locale.ROOT) |
|
|
|
public static int compare(PackagePartName obj1, PackagePartName obj2) { |
|
|
|
return compare ( |
|
|
|
obj1 == null ? null : obj1.getName(), |
|
|
|
obj2 == null ? null : obj2.getName() |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
@@ -560,49 +507,48 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
* numerical portion), but sorts "File10.png" before "file2.png" |
|
|
|
* (lexigraphical sort) |
|
|
|
*/ |
|
|
|
public static int compare(String str1, String str2) |
|
|
|
public static int compare(final String str1, final String str2) |
|
|
|
{ |
|
|
|
if (str1 == null) |
|
|
|
{ |
|
|
|
if (str1 == null) { |
|
|
|
// (null) == (null), (null) < (non-null) |
|
|
|
return (str2 == null ? 0 : -1); |
|
|
|
} |
|
|
|
else if (str2 == null) |
|
|
|
{ |
|
|
|
} else if (str2 == null) { |
|
|
|
// (non-null) > (null) |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
int len1 = str1.length(); |
|
|
|
int len2 = str2.length(); |
|
|
|
for (int idx1 = 0, idx2 = 0; idx1 < len1 && idx2 < len2; /*nil*/) |
|
|
|
{ |
|
|
|
char c1 = str1.charAt(idx1++); |
|
|
|
char c2 = str2.charAt(idx2++); |
|
|
|
|
|
|
|
if (Character.isDigit(c1) && Character.isDigit(c2)) |
|
|
|
{ |
|
|
|
int beg1 = idx1 - 1; // undo previous increment |
|
|
|
while (idx1 < len1 && Character.isDigit(str1.charAt(idx1))) |
|
|
|
{ |
|
|
|
++idx1; |
|
|
|
|
|
|
|
if (str1.equalsIgnoreCase(str2)) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
final String name1 = str1.toLowerCase(Locale.ROOT); |
|
|
|
final String name2 = str2.toLowerCase(Locale.ROOT); |
|
|
|
|
|
|
|
final int len1 = name1.length(); |
|
|
|
final int len2 = name2.length(); |
|
|
|
for (int idx1 = 0, idx2 = 0; idx1 < len1 && idx2 < len2; /*nil*/) { |
|
|
|
final char c1 = name1.charAt(idx1++); |
|
|
|
final char c2 = name2.charAt(idx2++); |
|
|
|
|
|
|
|
if (Character.isDigit(c1) && Character.isDigit(c2)) { |
|
|
|
final int beg1 = idx1 - 1; // undo previous increment |
|
|
|
while (idx1 < len1 && Character.isDigit(name1.charAt(idx1))) { |
|
|
|
idx1++; |
|
|
|
} |
|
|
|
|
|
|
|
int beg2 = idx2 - 1; // undo previous increment |
|
|
|
while (idx2 < len2 && Character.isDigit(str2.charAt(idx2))) |
|
|
|
{ |
|
|
|
++idx2; |
|
|
|
final int beg2 = idx2 - 1; // undo previous increment |
|
|
|
while (idx2 < len2 && Character.isDigit(name2.charAt(idx2))) { |
|
|
|
idx2++; |
|
|
|
} |
|
|
|
|
|
|
|
// note: BigInteger for extra safety |
|
|
|
int cmp = new BigInteger(str1.substring(beg1, idx1)).compareTo |
|
|
|
( |
|
|
|
new BigInteger(str2.substring(beg2, idx2)) |
|
|
|
); |
|
|
|
if (cmp != 0) return cmp; |
|
|
|
final BigInteger b1 = new BigInteger(name1.substring(beg1, idx1)); |
|
|
|
final BigInteger b2 = new BigInteger(name2.substring(beg2, idx2)); |
|
|
|
final int cmp = b1.compareTo(b2); |
|
|
|
if (cmp != 0) { |
|
|
|
return cmp; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (c1 != c2) |
|
|
|
{ |
|
|
|
else if (c1 != c2) { |
|
|
|
return (c1 - c2); |
|
|
|
} |
|
|
|
} |
|
|
@@ -610,6 +556,11 @@ public final class PackagePartName implements Comparable<PackagePartName> { |
|
|
|
return (len1 - len2); |
|
|
|
} |
|
|
|
|
|
|
|
private static boolean isDigitOrLetter(char c) { |
|
|
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); |
|
|
|
} |
|
|
|
|
|
|
|
private static boolean isHexDigit(char c) { |
|
|
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* ************************************************************************** */ |