/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* Licensed 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 net.ontopia.infoset.impl.basic;
import java.net.MalformedURLException;
import java.net.URL;
import net.ontopia.infoset.core.LocatorIF;
import net.ontopia.utils.OntopiaRuntimeException;
/**
* INTERNAL.
*/
@SuppressWarnings("serial")
public class URIFragmentLocator extends AbstractLocator {
protected String address; // shared
protected String fragment; // after '#'
protected short schemeEnd; // the ':' char in the scheme part
protected short authorityEnd; // last char in authority part
protected short lastSlash; // last slash in directory path
protected URIFragmentLocator(String address, String fragment,
short schemeEnd, short authorityEnd, short lastSlash) {
this.address = address;
this.fragment = fragment;
this.schemeEnd = schemeEnd;
this.authorityEnd = authorityEnd;
this.lastSlash = lastSlash;
}
/**
* INTERNAL: Returns the URI as a URL object.
*
* @deprecated Because not all URIs can be represented as URL objects.
*/
public URL getURL() throws MalformedURLException {
return new URL(address + "#" + fragment);
}
// --------------------------------------------------------------------------
// LocatorIF implementation
// --------------------------------------------------------------------------
public String getNotation() {
return "URI";
}
public String getAddress() {
return address + "#" + fragment;
}
public LocatorIF resolveAbsolute(String rel) {
int length = rel.length();
if (length == 0)
return this;
switch (rel.charAt(0)) {
case '#':
return new URIFragmentLocator(address, rel.substring(1), schemeEnd,
authorityEnd, lastSlash);
case '/':
if (length != 1 && rel.charAt(1) == '/') { // begins with "//"
if (authorityEnd == -1)
throw new OntopiaRuntimeException(new MalformedURLException(
"Base URI is not hierarchical"));
return new URILocator(address.substring(0, schemeEnd + 1) + rel,
schemeEnd, authorityEnd, lastSlash, (short) address.length());
} else
// FIXME: should normalize absolute path
return new URILocator(address.substring(0, authorityEnd) + rel,
schemeEnd, authorityEnd, lastSlash, (short) address.length());
} // no default needed; the rest of the method _is_ the default
try {
char[] relative = rel.toCharArray();
// does the URI have a scheme?
if (getScheme(relative) != -1)
return new URILocator(rel);
// scan for slashes in URI
int ix;
for (ix = 0; ix < length && relative[ix] != '/'; ix++)
;
// there were slashes, use constructor for unnormalized URIs,
// so that the normalizer resolves the directory for us
// (also do this if rel is "." or "..")
if (ix < length || rel.equals(".") || rel.equals("..")) {
if (lastSlash == -1) // no directory part
return new URILocator(address.substring(0, authorityEnd + 1) + rel);
else
return new URILocator(address.substring(0, lastSlash + 1) + rel);
}
// there were no slashes, so this is a pure file name
if (lastSlash == -1) // base has no directory part
return new URILocator(address.substring(0, authorityEnd + 1) + rel,
schemeEnd, authorityEnd, lastSlash, (short) address.length());
else
return new URILocator(address.substring(0, lastSlash + 1) + rel,
schemeEnd, authorityEnd, lastSlash, (short) address.length());
} catch (MalformedURLException e) {
throw new OntopiaRuntimeException(e);
}
}
public String getExternalForm() {
return URILocator.toExternalForm(address + "#" + fragment);
}
// -----------------------------------------------------------------------------
// Misc
// -----------------------------------------------------------------------------
/**
* Parses the scheme part of a URI.
*
* @return The index of the last char in the scheme, which will be ':' or -1
* if there is no scheme.
*/
private int getScheme(char[] uri) {
int index = 0;
while ((index < uri.length) && (uri[index] != '/') && (uri[index] != '?')
&& (uri[index] != ':') && (uri[index] != '#'))
index++;
if (index == 0 || index >= uri.length || uri[index] != ':')
return -1;
return index;
}
public int hashCode() {
// this hashCode() implementation returns the same value as
// return (address + "#" + fragment).hashCode();
// would have done, by carefully mimicking the hash code algorithm
// for java.lang.String objects given in the JDK javadoc. using
// it gives a 10% speed increase on import of topic maps
int hash = address.hashCode(); // base hash code
hash = 31 * hash + '#';
String frag = fragment;
int len = frag.length();
for (int i = 0; i < len; i++)
hash = 31 * hash + frag.charAt(i);
return hash;
}
public boolean equals(Object object) {
try {
if (object instanceof URIFragmentLocator) {
URIFragmentLocator locator = (URIFragmentLocator) object;
return fragment.equals(locator.fragment)
&& address.equals(locator.address);
} else {
LocatorIF locator = (LocatorIF) object;
return (address + "#" + fragment).equals(locator.getAddress())
&& locator.getNotation().equals("URI");
}
} catch (ClassCastException e) {
return false; // In case the object is not a locator
} catch (NullPointerException e) {
return false; // In case the object is null
}
}
}