/*
* Copyright (c) 2009-2015
* IT-Consulting Stephan Schloepke (http://www.schloepke.de/)
* klemm software consulting Mirko Klemm (http://www.klemm-scs.com/)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jbasics.utilities;
import org.jbasics.checker.ContractCheck;
import org.jbasics.pattern.factory.ParameterFactory;
import org.jbasics.types.sequences.Sequence;
import java.net.URI;
@SuppressWarnings("nls")
/**
* ---------------------- Copying the section from JAXB spec --------------------
* D.5.1 Mapping from a Namespace URI
* An XML namespace is represented by a URI. Since XML Namespace will be
* mapped to a Java package, it is necessary to specify a default mapping from a
* URI to a Java package name. The URI format is described in [RFC2396].
* The following steps describe how to map a URI to a Java package name. The
* example URI, http://www.acme.com/go/espeak.xsd, is used to
* illustrate each step.
* 1. Remove the scheme and ":" part from the beginning of the URI, if
* present.
* Since there is no formal syntax to identify the optional URI scheme, restrict
* the schemes to be removed to case insensitive checks for schemes
* "http" and "urn".
* //www.acme.com/go/espeak.xsd
* 2. Remove the trailing file type, one of .?? or .??? or .html.
* //www.acme.com/go/espeak
* 3. Parse the remaining string into a list of strings using '/' and ':' as
* separators. Treat consecutive separators as a single separator.
* {"www.acme.com", "go", "espeak" }
* 4. For each string in the list produced by previous step, unescape each escape
* sequence octet.
* {"www.acme.com", "go", "espeak" }
* 5. If the scheme is a "urn", replace all dashes, "-", occurring in the first
* component with ".".2
* 6. Apply algorithm described in Section 7.7 "Unique Package Names" in
* [JLS] to derive a unique package name from the potential internet domain
* name contained within the first component. The internet domain name is
* reversed, component by component. Note that a leading "www." is not
* considered part of an internet domain name and must be dropped.
* If the first component does not contain either one of the top-level
* domain names, for example, com, gov, net, org, edu, or one of the
* English two-letter codes identifying countries as specified in ISO
* Standard 3166, 1981, this step must be skipped.
* {"com", "acme", "go", "espeak"}
* 7. For each string in the list, convert each string to be all lower case.
* {"com", "acme", "go", "espeak" }
* 8. For each string remaining, the following conventions are adopted from
* [JLS] Section 7.7, "Unique Package Names."
* a. If the sting component contains a hyphen, or any other special
* character not allowed in an identifier, convert it into an underscore.
* b. If any of the resulting package name components are keywords then
* append underscore to them.
* c. If any of the resulting package name components start with a digit, or
* any other character that is not allowed as an initial character of an
* identifier, have an underscore prefixed to the component.
* {"com", "acme", "go", "espeak" }
* 9. Concatenate the resultant list of strings using '.' as a separating character
* to produce a package name.
* Final package name: "com.acme.go.espeak".
**/
public class NamespacePackageFactory implements ParameterFactory<String, URI> {
public static final NamespacePackageFactory SHARED_INSTANCE = new NamespacePackageFactory();
private static final String HTML_EXTENSION = "html";
private static final String HTTP_SCHEME = "http"; //$NON-NLS-1$
private static final String URN_SCHEME = "urn"; //$NON-NLS-1$
@Override
public String create(final URI param) {
if (param == null) {
return null;
} else if (DataUtilities.isInList(param.getScheme(), NamespacePackageFactory.HTTP_SCHEME, NamespacePackageFactory.URN_SCHEME)) {
String temp = param.getSchemeSpecificPart();
final int lastDot = temp.lastIndexOf('.');
if (lastDot >= 0 && lastDot < temp.length()) {
final String extension = temp.substring(lastDot + 1);
if (extension.length() == 2 || extension.length() == 3 || NamespacePackageFactory.HTML_EXTENSION.equalsIgnoreCase(extension)) {
temp = temp.substring(0, lastDot);
}
}
Sequence<String> parts = ContractCheck.mustNotBeNullOrEmpty(Sequence.split(temp, "/+|:+"), "uri-parts");
if (parts.first() == null || parts.first().length() == 0) {
parts = ContractCheck.mustNotBeNullOrEmpty(parts.rest(), "uri-parts");
}
parts = parts.apply(StringTransposers.URL_DECODE_TRANSPOSER);
return convertDomain(param.getScheme(), parts.first()).concat(parts.rest())
.apply(StringTransposers.TO_LOWERCASE_TRANSPOSER)
.apply(StringTransposers.JAVA_IDENTIFIER_TRANSPOSER)
.apply(StringTransposers.JAVA_KEYWORD_FILTER_TRANSPOSER)
.joinToString(".");
} else {
throw new IllegalArgumentException("Unsupported scheme " + param.getScheme());
}
}
private Sequence<String> convertDomain(final String scheme, String domain) {
if (NamespacePackageFactory.URN_SCHEME.equals(scheme)) {
domain = domain.replace('-', '.');
}
Sequence<String> domainParts = Sequence.split(domain, "\\.");
final Sequence<String> domainPartsBac = domainParts;
if ("www".equalsIgnoreCase(domainParts.first())) {
domainParts = domainParts.rest();
}
domainParts = domainParts.reverse();
final String topLevelDomain = domainParts.first();
if (topLevelDomain.length() != 2 && !DataUtilities.isInList(topLevelDomain, "com", "gov", "net", "org", "edu")) {
domainParts = domainPartsBac;
}
return domainParts;
}
}