/*
* Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
*
* THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
* WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. 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. SHOULD THE SOFTWARE PROVE DEFECTIVE
* IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
* CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
* NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
* DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
* THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
* USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
* PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
* AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
*/
package org.epics.css.dal.simulation;
import java.net.URI;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NamingException;
import com.cosylab.naming.Escaper;
import com.cosylab.naming.URIName;
/**
* <code>RemoteInfo</code> interface represents the target in the remote
* layer, be it an action, a query, a namespace etc. The most important
* feature of the <code>RemoteInfo</code> implementation is to produce a
* consistent representation of that remote target. In other words, target
* can be represented as an DAL URI, for instance<p><b>DAL-Simulator://manager/PSBEND_M.01/current</b></p>
* or it may be represented as a JNDI <code>javax.naming.Name</code>
* sequence:<p>[0]=DAL-Simulator [1]=manager [2]=PSBEND_M.01 [3]=current</p>
* or it may be a simple string. The default implementation allows all
* these conversions and is able to construct a new name from components like
* <code>remoteName</code>, URI <code>authority</code>, <code>plugType</code>
* and so on. For detailed documentation see <code>RemoteInfo</code> interface
* javadoc.<p>This implementation buffers the <code>URI</code>, so once it
* has been created for the first time, the invocations of
* <code>toURI()</code> return immediately. Moreover, this class implements
* <code>javax.naming.Name</code> directly.</p>
* <p>As an instance of <code>javax.naming.Name</code> this instance is
* unmodifiable, i.e. all methods adding or removing a name component will
* fail with an exception.</p>
* <p>Basically this RemoteInfo is constructed from following parts: plug
* type, authority, connection name and query. Plug type and connection name
* is obligatory. This RI implementation will try to use them unmodified as
* thay are defined in constructors. To enshure this, they must not contain
* directry escape characters (/ and ?), if they are not part of hirachical
* structure. Use constructor with directory Name and Bindings or convenience
* static methods to define RI parts when they need to be escaped for special
* characters. RI parts will be escaped when they are transformed to Nane and
* URI representation.</p>
*
* @author Gasper Tkacik
*/
public class RemoteInfo extends URIName
{
private static final long serialVersionUID = 5208467218927145986L;
private static final Escaper remoteInfoEscaper = new Escaper(new char[]{
'/', '?'
});
/** Prefix, which is used for concate URI schema part from plug type name. */
public static final String SCHEME_PREFIX = "DAL-";
/**
* Takes a single escaped part of an URI and unescapes it to reveal
* the true name component. The string parameter should not contain any
* special characters (such as '/', ':', '?', ';', '#' etc.) except for
* the '%' character which should be used for escaping only. Example:
* "Device%203%2F1" -> "Device 3/1"
*
* @param escapedPart the String that should be escaped
*
* @return String which can be used to form an URI
*/
public static String unescapePartOfURI(String escapedPart)
{
String ret = URI.create("DAL:///" + escapedPart).getPath().substring(1);
return ret;
}
@SuppressWarnings("unused")
private RemoteInfo()
{
// Hide constructor
}
/**
* Creates a new remote info object from the name of the remote entity, the
* authority and the plug type. This constructor will usually be used to
* name connectable entities. All string are expected to be encoded for
* nonhierarcical use of '/' or '?' characters.
*
* @param name the name of the connectable entity; this name may contain
* slash hierarchical separators, or end with query part, separated
* with '?' from remote name part; non-<code>null</code>
* @param authority an optional namespace authority (such as a name server,
* context server, device manager etc); i.e. that is the entity in
* the scope of which the <code>remoteName</code> becomes a unique
* remote target designation; may be <code>null</code> if the plug
* can either provide a default value or the system runs on top of
* a global namespace
* @param plugType the name of the plug under which this remote info is
* being issued; this is the plug where the remote target runs;
* non-<code>null</code>
*
* @throws NamingException if name creation fails
* @throws NullPointerException if name or plug type is <code>null</code>.
*/
public RemoteInfo(String name, String authority, String plugType)
throws NamingException
{
super();
assert (name != null);
assert (plugType != null);
if (name == null) {
throw new NullPointerException("name");
}
if (plugType == null) {
throw new NullPointerException("plugType");
}
String query = null;
int i = name.lastIndexOf('?');
if (i > -1) {
query = name.substring(i + 1);
name = name.substring(0, i);
}
initialize(SCHEME_PREFIX + plugType, authority, name, query);
}
/**
* Creates new RemoteInfo from URI instance.
*
* @param uri remote URI
*/
public RemoteInfo(URI uri)
{
super(uri);
}
/**
* Creates new RemoteInfo from URI instance.
*
* @param uri URI based implementation ov Name
*/
public RemoteInfo(URIName uri)
{
super(uri);
}
/**
* Creates new RemoteInfo by parsing URI string.
*
* @param uri URI string
*
* @throws NamingException if creation fails
*/
public RemoteInfo(String uri) throws NamingException
{
super(uri);
}
/**
* Creates a new remote info by appending both a hierarchical component and
* finally a query to the given <code>remoteName</code>. This constructor
* is used internally by the <code>create...()</code> methods of this
* class.
*
* @param remoteName the name of the connectable for which this remote info
* is to be created, may be hierarchical with slash separators
* @param query the query to be added right after the
* <code>component</code> part
* @param authority authority to use with this remote info, may be
* <code>null</code> if the plug will determine authority or if a
* global namespace is used
* @param plugType the type of the plug in the scope of which this info is
* going to be issued / valid, non-<code>null</code>
*
* @throws NamingException if creation fails
* @throws NullPointerException if plug type is null
*/
public RemoteInfo(String remoteName, String query, String authority,
String plugType) throws NamingException
{
super();
assert (plugType != null);
if (plugType == null) {
throw new NullPointerException("plugType");
}
initialize(SCHEME_PREFIX + plugType, authority, remoteName, query);
}
/**
* Creates a new remote info by appending both a hierarchical component and
* finally a query to the given <code>remoteName</code>. This constructor
* is used internally by the <code>create...()</code> methods of this
* class.
*
* @param parent name, from which to copy contents; non-<code>null</code>
* @param component the part of name that will be appended right after the
* <code>remoteName</code>, without slashes, non-<code>null</code>
* @param query the query to be added right after the
* <code>remoteName</code> part
*
* @throws InvalidNameException if creation fails
*/
public RemoteInfo(RemoteInfo parent, String component, String query)
throws InvalidNameException
{
super();
initialize(0, parent.isWithQuery() ? parent.size() - 1 : parent.size(),
parent.components, parent.relative, parent.withAuthority,
parent.withQuery);
if (component != null) {
super.add(component);
}
if (query != null) {
super.add(query);
setWithQuery(true);
}
}
/**
* Returns the name part of this remote info: the remote name with
* the query. This is the part of URI without the schema and authority.
*
* @return the name part of the remote info
*/
public String getName()
{
return extractName(toURI());
}
/**
* Returns the remote name part of this remote info. This is the
* part of URI without the schema, authority and query.
*
* @return the name part of the remote info
*/
public String getRemoteName()
{
return extractRemoteName(toURI());
}
/**
* Returns the plug name by which this remote info has been
* instantiated.
*
* @return name of the plug that instantiated this remote info
*/
public String getPlugType()
{
return extractPlugType(toURI());
}
/**
* Creates a new remote info instance by appending a new path
* component part into the name. For example, "abeans-Simulator:///PSBEND"
* plus "current" will result in "abeans-Simulator:///PSBEND/current".
* Quary part is not included in new RemoteInfo.
*
* @param componentName the name to append into the pathcomponent URI part,
* non-<code>null</code>
*
* @return a new remote info instance with appended path
*/
public RemoteInfo createHierarchy(String componentName)
{
try {
return new RemoteInfo(this, componentName, null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Creates a new remote info instance by appending a query string
* to the end of the name. For example,
* "abeans-Simulator:///PSBEND/current" plus query "get" will result in
* "abeans-Simulator:///PSBEND/current?get".
*
* @param queryName the query that will be appended after all pathcomponent
* parts of the URI name, non-<code>null</code>
*
* @return a new remote info instance with appended query
*/
public RemoteInfo createQuery(String queryName)
{
try {
return new RemoteInfo(this, null, queryName);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Returns the authority part of this remote info if it exists.
*
* @return authority or <code>null</code> if either the plug is calculating
* the authority on the fly or the plug is running on no-authority
* (global) namespace
*/
public String getAuthority()
{
return extractAuthority(toURI());
}
/**
* Creates a new remote info by appending to an existing remote
* name both a hierarchy pathcomponent and a new query. Other properties
* of the existing remote info are retained (such as the authority, plug
* type etc).
*
* @param componentName the component to append to the existing remote
* name, non-<code>null</code>
* @param queryName the query to append to the end of pathcomponent
* sequence, non-<code>null</code>
*
* @return a new instance of remote info with names appended
*/
public RemoteInfo createHierarchyAndQuery(String componentName,
String queryName)
{
try {
return new RemoteInfo(this, componentName, queryName);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Throws an exception.
*
* @param posn ignored
* @param comp ignored
*
* @return exception always thrown
*
* @throws InvalidNameException always thrown
*/
@Override
public Name add(int posn, String comp) throws InvalidNameException
{
throw new InvalidNameException(
"Cannot add to a remote info with generic add method. Use 'create...' methods instead.");
}
/**
* Throws an exception.
*
* @param comp ignored
*
* @return exception always thrown
*
* @throws InvalidNameException always thrown
*/
@Override
public Name add(String comp) throws InvalidNameException
{
throw new InvalidNameException(
"Cannot add to a remote info with generic add method. Use 'create...' methods instead.");
}
/**
* Throws an exception.
*
* @param posn ignored
* @param n ignored
*
* @return exception always thrown
*
* @throws InvalidNameException always thrown
*/
@Override
public Name addAll(int posn, Name n) throws InvalidNameException
{
throw new InvalidNameException(
"Cannot add to a remote info with generic add method. Use 'create...' methods instead.");
}
/**
* Throws an exception.
*
* @param suffix ignored
*
* @return exception always thrown
*
* @throws InvalidNameException always thrown
*/
@Override
public Name addAll(Name suffix) throws InvalidNameException
{
throw new InvalidNameException(
"Cannot add to a remote info with generic add method. Use 'create...' methods instead.");
}
/**
* Throws an exception.
*
* @param posn ignored
*
* @return exception always thrown
*
* @throws InvalidNameException always thrown
*/
@Override
public Object remove(int posn) throws InvalidNameException
{
throw new InvalidNameException(
"Cannot remove name elements from remote info.");
}
/**
* Returns the query part of this remote info if it exists.
*
* @return query or <code>null</code>
*/
public String getQuery()
{
return extractQuery(toURI());
}
/**
* Creates Name instance by parsing URI string.
*
* @param uri URI string
*
* @return Name instance
*
* @throws NamingException if Name creation fails
*/
public static Name parseURIstring(String uri) throws NamingException
{
return new URIName(uri);
}
/**
* Parses remote name to relative URIName. Remote name corresponds
* to path part of URI.
*
* @param remoteName remote name, a relative path in URI with no
*
* @return relative URIName
*/
public static Name parseRemoteName(String remoteName)
{
String[] comps = remoteInfoEscaper.stringToComponents(remoteName);
URIName ret = new URIName(comps);
ret.setRelative(true);
return ret;
}
/**
* Parses Abeans URI string to URIName.
*
* @param uri URI sting
* @param relative flags URIName to be relative or not
*
* @return instance of URIName
*/
public static Name parseAbeansURI(String uri, boolean relative)
{
String[] comps = remoteInfoEscaper.stringToComponents(uri);
URIName ret = new URIName(comps);
ret.setRelative(relative);
return ret;
}
/**
* Takes a single unescaped part of a hierarchical name and escapes
* it so that it can be used to form remoteName part of remoteInfo. Escape
* rules are similar to those in javax.naming.CompositeName, only that '?'
* character is added as a separator and is escaped in the same way as '/'
* character. Example "Device 3/1" -> "Device 3\/1"
*
* @param string which should be escaped to represent a part of a Name
* instance
*
* @return an escaped string, which can be concatenated with '/' and '?' to
* form remoteName part of RemoteInfo
*/
public static String escapePartToRemoteName(String string)
{
return remoteInfoEscaper.escapeComponent(string);
}
/**
* Extracts name from <code>URI</code>.<p>This method helps
* creating <code>RemoteInfo</code> from <code>URI</code> serialized form.</p>
*
* @param uri the target <code>URI</code> for remote entity or operation
*
* @return extracted Abeans name
*/
public static String extractRemoteName(URI uri)
{
if (uri == null) {
return null;
}
String path = uri.getPath();
if (path != null && path.length() > 0) {
if (path.charAt(0) == '/') {
return path.substring(1);
}
return path;
}
return null;
}
/**
* Extracts remote name from <code>URI</code>. Extracted
* nametontains remote entity name plus query.<p>This method helps
* creating <code>RemoteInfo</code> from <code>URI</code> serialized form.</p>
*
* @param uri the target <code>URI</code> for remote entity or operation
*
* @return extracted Abeans name
*/
public static String extractName(URI uri)
{
if (uri == null) {
return null;
}
String path = uri.getPath();
String query = uri.getQuery();
int l = path != null ? path.length() : 0;
l += query != null ? query.length() : 0;
l++;
StringBuffer sb = new StringBuffer(l);
if (path != null && path.length() > 0) {
if (path.charAt(0) == '/') {
sb.append(path.toCharArray(), 1, path.length() - 1);
} else {
sb.append(path.toCharArray());
}
}
if (uri.getQuery() != null) {
sb.append('?');
sb.append(uri.getQuery());
}
return sb.length() > 0 ? sb.toString() : null;
}
/**
* Extracts authority part of URI for Abeans entity.<p>This method
* helps creating <code>RemoteInfo</code> from <code>URI</code> serialized
* form.</p>
*
* @param uri the target <code>URI</code> for Abeans Entity
*
* @return extracted Abeans authority
*/
public static String extractAuthority(URI uri)
{
if (uri == null) {
return null;
}
return uri.getAuthority();
}
/**
* Extracts query part of URI for Abeans entity.<p>This method
* helps creating <code>RemoteInfo</code> from <code>URI</code> serialized
* form.</p>
*
* @param uri the target <code>URI</code> for Abeans Entity
*
* @return extracted Abeans query
*/
public static String extractQuery(URI uri)
{
if (uri == null) {
return null;
}
return uri.getQuery();
}
/**
* Extracts plug type from scheme part of URI for Abeans entity.<p>This
* method helps creating <code>RemoteInfo</code> from <code>URI</code>
* serialized form.</p>
*
* @param uri the target <code>URI</code> for Abeans Entity
*
* @return extracted Abeans plug type
*/
public static String extractPlugType(URI uri)
{
if (uri == null) {
return null;
}
String scheme = uri.getScheme();
if (scheme == null) {
return null;
}
int i = SCHEME_PREFIX.length();
if (scheme.length() > i) {
return scheme.substring(i);
}
return null;
}
} /* __oOo__ */
/* __oOo__ */