/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.stanbol.commons.namespaceprefix;
import java.util.regex.Pattern;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.osgi.service.cm.ConfigurationException;
public final class NamespaceMappingUtils {
/**
* Restrict instantiation
*/
private NamespaceMappingUtils() {}
/**
* This pattern checks for invalid chars within an prefix.
* The used pattern is <code>[^a-zA-Z0-9\-_]</code>. Meaning that prefixes
* are allows to include alpha numeric characters including '-' and '_'
*/
private static final Pattern PREFIX_VALIDATION_PATTERN = Pattern.compile("[^a-zA-Z0-9\\-_]");
/**
* Getter for the prefix for the parsed {prefix}:{localName} value.
* If the parsed value does not follow this pattern (but is an URI) than
* this method will return <code>null</code>
* @param shortNameOrUri the shortName or an URI
* @return the prefix or <code>null</code> if an URI was parsed
*/
public static String getPrefix(String shortNameOrUri){
//ignore null and empty strings
if(shortNameOrUri == null || shortNameOrUri.isEmpty()) {
return null; //not a short uri
}
int index = shortNameOrUri.indexOf(':');
if(index < 0 && shortNameOrUri.charAt(0) != '/'){
return ""; //default namespace
} else if (index > 0){
if(shortNameOrUri.length() == (index+1) || //{prefix}: was parsed
shortNameOrUri.charAt(index+1) == '/' ||
(index == 3 && shortNameOrUri.startsWith("urn"))){
return null; // URI was parsed ({protocol}:/...)
} else {
return shortNameOrUri.substring(0, index);
}
} else {
return null; //not a short name
}
}
/**
* Extracts the namespace form the parsed URI or returns <code>null</code>
* of the URI does not contain an namesoace (e.g. http://www.test.org,
* urn:someValue)
* @param uri the uri
* @return the namespace including the separator ('#' or '/' or ':')
*/
public static String getNamespace(String uri){
if(uri == null){
return uri;
}
final int index;
if(uri.startsWith("urn:")){
index = uri.lastIndexOf(':');
if(index < 5){ //urn:?: is the shortest possible namesoace
return null;
}
} else{
int protocolIndex = uri.indexOf(":/");
if(protocolIndex < 1){
return null; //not an absolute URI
}
index = Math.max(uri.lastIndexOf('#'),uri.lastIndexOf('/'));
if(protocolIndex + 3 > index) { //in '{port}://' the 2nd '/' is no namespace
return null;
}
}
//do not convert if the parsed uri does not contain a local name
if(index > 0) {// and the namespace is not the protocol
return uri.substring(0, index+1);
} else {
return null;
}
}
/**
* Uses the NamespacePrefixService#PREFIX_VALIDATION_PATTERN to check
* if the parsed prefix is valid
* @param prefix the prefix to check
* @return <code>true</code> if valid. Othervise <code>false</code>
*/
public static boolean checkPrefix(String prefix){
if(prefix == null){
return false;
}
return !PREFIX_VALIDATION_PATTERN.matcher(prefix).find();
}
/**
* Checks of the parsed namespace is valid. Namespaces starting with
* '<code>urn:</code>' need to end with ':'. Otherwise namespaces need to
* end with '/' or '#' and contain a protocol (checked by searching ':/').
* No further validation on the parsed namespace are done
* @param namespace the namespace
* @return <code>true</code> if valid. Othervise <code>false</code>
*/
public static boolean checkNamespace(String namespace){
if(namespace == null || namespace.isEmpty()){
return false;
}
return namespace.startsWith("urn:") ?
namespace.charAt(namespace.length()-1) == ':' :
(namespace.charAt(namespace.length()-1) == '#' ||
namespace.charAt(namespace.length()-1) == '/') &&
namespace.indexOf(":/") > 0;
}
/**
* Utility intended to be used during activate of OSGI components that support
* the use of '{prefix}:{localname}' in its configurations. The
* {@link NamespacePrefixService} is assumed as optional so that users can
* use <code>ReferenceCardinality.OPTIONAL_UNARY</code> to inject the service.
* <p>
* Here is an example
* <code>
* Reference(cardinality=ReferenceCardinality.OPTIONAL_UNARY)
* protected NamespacePrefixService nps;
* </code>
* @param nps the {@link NamespacePrefixService} or <code>null</code> if not
* available
* @param property the configuration property (used for creating {@link ConfigurationException}s)
* @param value configured value. Might be both a '{prefix}:{localname}' or the full URI.
* @return the full URI
* @throws ConfigurationException if the conversion was not possible because
* the prefix is <code>null</code> or the prefix is
* unknown to the service
*/
public static String getConfiguredUri(NamespacePrefixService nps, String property, String value) throws ConfigurationException{
if(nps != null){
String fieldUri = nps.getFullName(value);
if(fieldUri == null){
throw new ConfigurationException(property, "The prefix '"
+ NamespaceMappingUtils.getPrefix(value)+"' is unknown (not mapped to an "
+ "namespace) by the Stanbol Namespace Prefix Mapping Service. Please "
+ "change the configuration to use the full URI instead of '"+value+"'!");
}
return fieldUri;
} else if(NamespaceMappingUtils.getPrefix(value) != null){
throw new ConfigurationException(property, "'{prefix}:{localname}' configurations "
+ "such as '"+value+"' are only supported if the NamespacePrefixService is "
+ "available for the Stanbol instance (what is currently not the case). Please "
+ "change the configuration to use the full URI");
} else { //no service but a full uri
return value;
}
}
/**
* Utility intended to be used to by components that do allow the use of
* '{prefix}:{localname}' in its configurations. The {@link NamespacePrefixService}
* is considered optional.
* @param nps the {@link NamespacePrefixService} or <code>null</code> if not
* available
* @param value configured value. Might be both a '{prefix}:{localname}' or the full URI.
* @return the full URI
* @throws IllegalArgumentException if the conversion was not possible because
* the prefix is <code>null</code> or the prefix is
* unknown to the service
*/
public static String getConfiguredUri(NamespacePrefixService nps, String value) throws IllegalArgumentException {
if(nps != null){
String fieldUri = nps.getFullName(value);
if(fieldUri == null){
throw new IllegalArgumentException("The prefix '"
+ NamespaceMappingUtils.getPrefix(value)+"' is unknown (not mapped to an "
+ "namespace) by the Stanbol Namespace Prefix Mapping Service. Please "
+ "change the configuration to use the full URI instead of '"+value+"'!");
}
return fieldUri;
} else if(NamespaceMappingUtils.getPrefix(value) != null){
throw new IllegalArgumentException("'{prefix}:{localname}' configurations "
+ "such as '"+value+"' are only supported if the NamespacePrefixService is "
+ "available for the Stanbol instance (what is currently not the case). Please "
+ "change the configuration to use the full URI");
} else { //no service but a full uri
return value;
}
}
}