/**
*
*/
package org.openrosa.client.jr.core.reference;
import java.util.Vector;
/**
* <p>The reference manager is a singleton class which
* is responsible for deriving reference URI's into
* references at runtime.</p>
*
* <p>Raw reference factories
* (which are capable of actually creating fully
* qualified reference objects) are added with the
* addFactory() method. The most common method
* of doing so is to implement the PrefixedRootFactory
* as either a full class, or an anonymous inner class,
* providing the roots available in the current environment
* and the code for constructing a reference from them.</p>
*
* <p>RootTranslators (which rely on other factories) are
* used to describe that a particular reference style (generally
* a high level reference like "jr://media/" or "jr://images/"
* should be translated to another available reference in this
* environment like "jr://file/". Root Translators do not
* directly derive references, but rather translate them to what
* the reference should look like in the current circumstances.</p>
*
* @author ctsims
*
*/
public class ReferenceManager {
private static ReferenceManager instance;
private Vector<RootTranslator> translators;
private Vector<ReferenceFactory> factories;
private ReferenceManager() {
translators = new Vector<RootTranslator>();
factories = new Vector<ReferenceFactory>();
}
/**
* @return Singleton accessor to the global
* ReferenceManager.
*/
public static ReferenceManager _() {
if(instance == null) {
instance = new ReferenceManager();
}
return instance;
}
/**
* @return The available reference factories
*/
public ReferenceFactory[] getFactories() {
ReferenceFactory[] roots = new ReferenceFactory[translators.size()];
translators.copyInto(roots);
return roots;
}
/**
* Adds a new Translator to the current environment.
* @param translator
*/
public void addRootTranslator(RootTranslator translator) {
if(!translators.contains(translator)) {
translators.addElement(translator);
}
}
/**
* Adds a factory for deriving reference URI's into references
* @param factory A raw ReferenceFactory capable of creating
* a reference.
*/
public void addReferenceFactory(ReferenceFactory factory) {
if(!factories.contains(factory)) {
factories.addElement(factory);
}
}
/**
* Derives a global reference from a URI in the current environment.
*
* @param uri The URI representing a global reference.
* @return A reference which is identified by the provided URI.
* @throws InvalidReferenceException If the current reference could
* not be derived by the current environment
*/
public Reference DeriveReference(String uri) throws InvalidReferenceException {
return DeriveReference(uri, (String)null);
}
/**
* Derives a reference from a URI in the current environment.
*
* @param uri The URI representing a reference.
* @param context A reference which provides context for any
* relative reference accessors.
* @return A reference which is identified by the provided URI.
* @throws InvalidReferenceException If the current reference could
* not be derived by the current environment
*/
public Reference DeriveReference(String uri, Reference context) throws InvalidReferenceException {
return DeriveReference(uri, context.getURI());
}
/**
* Derives a reference from a URI in the current environment.
*
* @param uri The URI representing a reference.
* @param context A reference URI which provides context for any
* relative reference accessors.
* @return A reference which is identified by the provided URI.
* @throws InvalidReferenceException If the current reference could
* not be derived by the current environment, or if the context URI
* is not valid in the current environment.
*/
public Reference DeriveReference(String uri, String context) throws InvalidReferenceException {
//Relative URI's need to determine their context first.
if(isRelative(uri)) {
//Clean up the relative reference to lack any leading separators.
if(uri.startsWith("./")) {
uri = uri.substring(2);
}
if(context == null ) {
throw new RuntimeException("Attempted to retrieve local reference with no context");
} else {
return derivingRoot(context).derive(uri, context);
}
} else {
return derivingRoot(uri).derive(uri);
}
}
private ReferenceFactory derivingRoot(String uri) throws InvalidReferenceException {
//First, try any/all roots referenced at runtime.
for(RootTranslator root : translators) {
if(root.derives(uri)) {
return root;
}
}
//Now try all of the raw connectors available
for(ReferenceFactory root : factories) {
if(root.derives(uri)) {
return root;
}
}
throw new InvalidReferenceException("No reference could be created for URI " + uri, uri);
}
/**
* @param URI
* @return Whether the provided URI describe a relative reference.
*/
public static boolean isRelative(String URI) {
if(URI.startsWith("./")) {
return true;
}
return false;
}
}