All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openmdx.base.naming.XRI_2Marshaller Maven / Gradle / Ivy

There is a newer version: 2.18.10
Show newest version
/*
 * ====================================================================
 * Project:     openMDX, http://www.openmdx.org/
 * Description: XRI 2 Marshaller 
 * Owner:       OMEX AG, Switzerland, http://www.omex.ch
 * ====================================================================
 *
 * This software is published under the BSD license as listed below.
 * 
 * Copyright (c) 2004-2014, OMEX AG, Switzerland
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 * 
 * * Neither the name of the openMDX team nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * ------------------
 * 
 * This product includes software developed by other organizations as
 * listed in the NOTICE file.
 */
package org.openmdx.base.naming;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

import org.openmdx.base.exception.RuntimeServiceException;
import org.openmdx.base.exception.ServiceException;
import org.openmdx.base.marshalling.Marshaller;
import org.openmdx.base.text.conversion.URITransformation;
import org.openmdx.base.text.conversion.UnicodeTransformation;
import org.openmdx.kernel.exception.BasicException;
import org.openmdx.kernel.xri.XRI_2Protocols;
import org.openmdx.kernel.xri.XRIAuthorities;

/**
 * XRI 2 Marshaller
 */
public final class XRI_2Marshaller
    implements Marshaller
{

    private XRI_2Marshaller(
    ){
        // Avoid external instantiation
    }
    
    /**
     * Memorize the singleton
     */ 
    final static private Marshaller instance = new XRI_2Marshaller();
    
    /**
     * xri://@openmdx
     */
    final static String[] ROOT = {};

    /**
     * 
     */
    final Pattern OPENMDX_OBJECT_AUTHORITY = Pattern.compile(
        XRIAuthorities.OPENMDX_AUTHORITY + "[*][A-Za-z0-9\\-]+([.][A-Za-z0-9\\-]+)*"
    );
    
    /**
     * Return the singleton
     */ 
    static public Marshaller getInstance(
    ){
        return XRI_2Marshaller.instance;
    }

    
    //------------------------------------------------------------------------
    // Implements Marshaller
    //------------------------------------------------------------------------

    /**
     * Marshal a CharSequence[] into a CharSequence
     * 
     * @param     charSequences
     *            The array of CharSequences to be marshalled.
     * 
     * @return      A CharSequence containing the marshalled objects.
   */
    public Object marshal (
        Object charSequences
    ) throws ServiceException {
        if (charSequences == null) return null;
        Object[]segments = (Object[])charSequences;        
        StringBuilder oid = new StringBuilder(XRI_2Protocols.OPENMDX_PREFIX);
        char separator = '*';
        for(
            int i = 0;
            i < segments.length;
            i++
        ){
            String segment = segments[i].toString();
            if(i > 0 || !segment.startsWith("!")) {
                oid.append(
                    separator
                );
            }
            if(isForeignCrossReference(segment)) {
                //
                // Foreign Cross Reference
                //
                encode(
                    segment, // source
                    oid, // target
                    i == 0, // authority
                    "", // prefix
                    "" // suffix
                );
            } else if (segment.indexOf('/') < 0) {
                //
                // No Cross Reference
                //
                int percent = segment.indexOf('%');
                if(percent >= 0) {
                    //
                    // Percent Sign
                    //
                    if(percent+1 == segment.length() && i+1 == segments.length) {
                        //
                        // Trailing Percent Sign (leads to "$..." wildcard)
                        //
                        if(segment.length() == 1) {
                            oid.append(
                                "($...)"
                            );
                        } else {
                            List subSegments = parseSubSegments(true, segment.substring(0, segment.length() - 1));
                            if(subSegments == null) {
                                appendCaseExactString(
                                    oid,
                                    segment
                                );
                            } else {
                                StringBuilder target = new StringBuilder();
                                boolean first = true;
                                for(
                                     Iterator j = subSegments.iterator();
                                     j.hasNext();
                                     first = false
                                ){
                                     String subSegment = j.next();
                                     String value = first && subSegment.startsWith("*") ? subSegment.substring(1) : subSegment;
                                     if(j.hasNext()) {
                                         target.append(value);
                                     } else {
                                         if(!first) {
                                             target.append('*');
                                         }
                                         if (value.length() > 0) {
                                             target.append(
                                                 "($.*"
                                             ).append(
                                                 value
                                             ).append(
                                                 ")*"
                                             );
                                         }
                                         encode(
                                             target.toString(), // source
                                             oid, // target
                                             i == 0, // authority
                                             "", // prefix
                                             "($..)/($...)" // suffix
                                         );
                                     }
                                }
                            }
                        }
                    } else {
                        //
                        // Embedded Percent Sign (requires escaping)
                        //
                        appendCaseExactString(
                            oid,
                            segment
                        );
                    }
                } else if (segment.startsWith(":") && segment.endsWith("*")) {
                    //
                    // "$.." wildcard
                    //
                    if (segment.length() == 2) {
                        oid.append(
                            "($..)"
                        );
                    } else {
                        encode(
                            segment.substring(1, segment.length() - 1),
                            oid, // target
                            i == 0, // authority
                            "($.*", // prefix
                            ")*($..)" // suffix
                        );
                    }
                } else {
                    encode(
                        segment, // source
                        oid, // target
                        i == 0, // authority
                        "", // prefix
                        "" // suffix
                    );
                }
            } else {
                //
                // openMDX cross reference
                //
                try {
                    Object xRef = marshal(new Path(segment).getSuffix(0));
                    oid.append(
                        '('
                    ).append(
                        xRef.toString().substring(XRI_2Protocols.SCHEME_PREFIX.length())
                    ).append(
                        ')'
                    );
                } catch (Exception exeption) {
                    appendCaseExactString(
                        oid,
                        segment
                    );
                }
            }
            separator = '/';
        }
        return oid.toString();
    }

	static String xriRepresentationOfGeneralSegment(
		boolean authority,	
		String classicRepresentation
	) {
		StringBuilder xriRepresentation = new StringBuilder();
		if(isForeignCrossReference(classicRepresentation)) {
		    //
		    // Foreign Cross Reference
		    //
		    encode(
		        classicRepresentation, // source
		        xriRepresentation, // target
		        authority, // authority
		        "", // prefix
		        "" // suffix
		    );
		} else {
		    //
		    // No Cross Reference
		    //
		    int percent = classicRepresentation.indexOf('%');
		    if(percent >= 0) {
	            //
	            // Embedded Percent Sign (requires escaping)
	            //
	            appendCaseExactString(
	                xriRepresentation,
	                classicRepresentation
	            );
		    } else {
		        encode(
		            classicRepresentation, // source
		            xriRepresentation, // target
			        authority, // authority
		            "", // prefix
		            "" // suffix
		        );
		    }
		}
		return xriRepresentation.toString();
	}
    
    /**
     * The following, then, are the XRI-specific steps required to convert an XRI into a URI.
     * 
    *
  1. Escape all percent '%' characters within a segment as "%25". *
  2. Escape unbalanced paranthesis within a segment as "%28" and "%29". *
  3. Escape all number sign '#' characters that appear within a segment as "%23". *
  4. Escape all question mark '?' characters that appear within a segment as "%3F". *
  5. Escape all slash '/' characters that appear within a segment as "%2F". *
* @param oid * @param authority * @param prefix * @param suffix * @param charSequence */ private static void encode ( String source, StringBuilder oid, boolean authority, String prefix, String suffix ){ oid.append(prefix); String xriSegment = authority ? source.replace(':', '.') : source; if(isCaseExactString(source)) { appendCaseExactString(oid, xriSegment); } else { Segment segment = new Segment( true, authority, source ); if(segment.isValid()) { oid.append(xriSegment); } else { appendCaseExactString(oid, xriSegment); } } oid.append(suffix); } /** * Unmarshal a CharSequence into a CharSequence[]. * * @param marshalledObjects * A string containing a marshalled sequence of objects * * @return A String array containing the unmarshaled sequence * of objects. * @exception ServiceException ILLEGAL_ARGUMENT */ public Object unmarshal ( Object charSequence ) throws ServiceException { String xriString = SpecialResourceIdentifiers.unescapeResourceIdentifier(charSequence.toString()); try { boolean treeWildcard = xriString.endsWith(")/($...)"); List segments = getObjectIdentifier(false, xriString); String authority = segments.get(0).toString(); if(XRIAuthorities.OPENMDX_AUTHORITY.equals(authority)) return ROOT; String[] reply = new String[ segments.size() - (treeWildcard ? 1 : 0) ]; reply[0] = authority.charAt(8) == '*' ? authority.substring(9).replaceAll( "\\(\\$\\.\\.\\.\\)", "%" ).replaceAll( "\\(\\$\\.\\.\\)", ":*" ).replaceAll( "\\(\\$\\./([^\\)]+)\\)", "\1:*" ).replaceAll( "\\(\\$\\.\\)", ":*" ).replaceAll( "\\.", ":" ) : authority.substring(8); for( int i = 1; i < reply.length; i++ ) { Segment segment = segments.get(i); List subSegments = segment.getSubSegments(); int jLimit = subSegments.size(); boolean subSegmentsWildcard = segment.getSubSegmentAt(jLimit - 1).equals("*($..)"); StringBuilder target = new StringBuilder(); boolean parameter = false; boolean wildcard = false; SubSegments: for( int j = 0; j < jLimit; j++ ) { boolean wasParameter = parameter; boolean wasWildcard = wildcard; parameter = false; wildcard = false; String subSegment = segment.getSubSegmentAt(j); String source = subSegment.toString().substring(1); String delimiter = wasParameter ? "=" : subSegment.startsWith("!") ? "!" : j == 0 ? "" : "*"; parameter = false; if(subSegment.startsWith("(", 1)) { List xriReference = parseCrossReference(false, subSegment); if (xriReference != null) { Segment authorityPath = xriReference.get(0); if(authorityPath == null) { target.append(delimiter).append(source); } else { String xrefAuthority = authorityPath.toString(); if("$..".equals(xrefAuthority)) { if(i + 1 == reply.length && treeWildcard) { target.append(":%"); } else if(j == 0) { target.append(":*"); } else if(!wasWildcard) { target.append(subSegment); } } else if ("$.".equals(xrefAuthority)) { target.append(subSegment); } else if (xrefAuthority.startsWith("$.*")) { if(i + 1 == reply.length && treeWildcard) { target.append( xrefAuthority.substring(3) ).append( '%' ); break SubSegments; } else if (j + 2 == jLimit && subSegmentsWildcard) { target.append( ':' ).append( xrefAuthority.substring(3) ).append( '*' ); wildcard = true; } else { target.append(subSegment); } } else if (xrefAuthority.startsWith("$.!")) { if(i + 1 == reply.length && treeWildcard) { target.append( xrefAuthority.substring(2) ).append( '%' ); break SubSegments; } else if (j + 1 == jLimit && subSegmentsWildcard) { target.append( ':' ).append( xrefAuthority.substring(2) ).append( '*' ); wildcard = true; } else { target.append(subSegment); } } else if ("$...".equals(xrefAuthority)) { target.append('%'); } else if (OPENMDX_OBJECT_AUTHORITY.matcher(xrefAuthority).matches()) { String embeddedXri = source.substring(1, source.length() - 1); target.append( delimiter ).append( new Path( embeddedXri.startsWith(XRI_2Protocols.SCHEME_PREFIX) ? embeddedXri : XRI_2Protocols.SCHEME_PREFIX + embeddedXri ).toClassicRepresentation() ); } else { target.append( delimiter ).append( source ); } } } else { target.append(delimiter).append(source); } } else { target.append(delimiter).append(source); } } reply[i] = unescape(target.toString()); } return reply; } catch (RuntimeServiceException exception) { throw new ServiceException( exception, BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.TRANSFORMATION_FAILURE, "Input is not a valid resource identifier", new BasicException.Parameter(BasicException.Parameter.XRI, xriString) ); } } private String unescape( String source ){ return isCaseExactString(source) ? URITransformation.decode(source.substring(8, source.length() - 1)) : source; } private static boolean isCaseExactString( String subSegment ){ return subSegment.startsWith("($t*ces*") && subSegment.indexOf(')') == subSegment.length() - 1; } /** * Test whether the parenthesis are balanced * * @param segment * * @return true if the segment could be an XRef */ private static boolean isForeignCrossReference( String segment ){ if( segment.startsWith("(") && segment.endsWith(")") ){ int open = 0; for( int i = 0, iLimit = segment.length(); i < iLimit; i++ ) { char c = segment.charAt(i); if(c == '(') { ++open; } else if (c == ')') { if(--open < 0) return false; } } return open == 0; } else { return false; } } private static void appendCaseExactString( StringBuilder target, String source ){ target.append( "($t*ces*" ); byte[] utf8 = UnicodeTransformation.toByteArray(source); for( int i = 0; i < utf8.length; i++ ){ byte octet = utf8[i]; if( (octet >= 'A' && octet <= 'Z') || (octet >= 'a' && octet <= 'z') || (octet >= '0' && octet <= '9') || (octet == '-' || octet == '.' || octet == '_' || octet == '~') ) { target.append((char)octet); } else { int digits = 0xFF & octet; target.append( '%' ).append( characterForDigit(digits / 0x10) ).append( characterForDigit(digits % 0x10) ); } } target.append( ')' ); } private final static char characterForDigit( int digit ){ return (char) ( digit < 10 ? '0' + digit : 'A' - 10 + digit ); } /** * Tests whether the String represents an openMDX object identifier * * @param lenient tells whether exceptions are thrown or mapped to null return values * @param xri * @param lenient tells whether exceptions are thrown or mapped to null return values * * @return true if the String repesents an openMDX object identifier * @throws ServiceException */ private static List getObjectIdentifier( boolean lenient, String xri ){ return xri.startsWith(XRI_2Protocols.OPENMDX_PREFIX) ? getExtensibleResourceIdentifier(lenient, xri) : null; } /** * Tests whether the String represents an absolute extensible resource identifier without scheme * * @param lenient tells whether exceptions are thrown or mapped to null return values * @param resourceIdentifier * * @return true if the String represents an absolute extensible resource identifier without scheme * @throws ServiceException */ private static List getExtensibleResourceIdentifier( boolean lenient, String resourceIdentifier ){ final String xri; if(resourceIdentifier.startsWith(XRI_2Protocols.SCHEME_PREFIX)) { xri = resourceIdentifier.substring(XRI_2Protocols.SCHEME_PREFIX.length()); } else { xri = resourceIdentifier; } if(xri.startsWith("!")) { // // pgcs-authority // List segments = parseSegments(lenient, xri); if(segments == null || !segments.isEmpty()) { if(lenient) { return null; } else { throw new RuntimeServiceException( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.TRANSFORMATION_FAILURE, "The global context symbol '!' must be followed by a non-empty authority segment", new BasicException.Parameter(BasicException.Parameter.XRI, xri), new BasicException.Parameter("position", 0) ); } } else { List authority = segments.get(0).xriSubSegments; return authority.isEmpty() || authority.get(0).length() <= 1 ? null : segments; } } else if(xri.startsWith("=") || xri.startsWith("@") || xri.startsWith("+") || xri.startsWith("$")) { // // rgcs-authority // return parseSegments(lenient, xri); } else { // // NO gcs-authority // if(lenient) { return null; } else { throw new RuntimeServiceException( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.TRANSFORMATION_FAILURE, "The XRI should start with a valid context symbol", new BasicException.Parameter(BasicException.Parameter.XRI, xri), new BasicException.Parameter("position", 0), new BasicException.Parameter("expected", "!", "=", "@", "+", "$") ); } } } /** * Retrieve the segments of an absolute extensible resource identifier without scheme * * @param lenient tells whether exceptions are thrown or mapped to null return values * @param path * * @return the segments of an absolute extensible resource identifier without scheme */ private static List parseSegments( boolean lenient, String path ){ List segments = new ArrayList(); int xRef = 0; int anchor = 0; for( int i = 1, limit = path.length(); i < limit; i++ ){ char c = path.charAt(i); if(c == '(') { xRef++; } else if(xRef == 0) { if(c == '/') { Segment segment = new Segment( lenient, anchor == 0, path.substring(anchor, i) ); if(segment.isValid()) { segments.add(segment); anchor = i + 1; } else { return null; } } else if (c == ')') { if(lenient) { return null; } else { throw new RuntimeServiceException( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.TRANSFORMATION_FAILURE, "The XRI contains a closing parenthesis without matching opening parenthesis", new BasicException.Parameter(BasicException.Parameter.XRI, path), new BasicException.Parameter("position", i) ); } } } else if(c == ')') { xRef--; } } Segment segment = new Segment( lenient, anchor == 0, path.substring(anchor) ); if(xRef == 0 && segment.isValid()) { segments.add(segment); return segments; } else { return null; } } /** * Retrieve the sub-segments * * @param lenient tells whether exceptions are thrown or mapped to null return values * @param segment * * @return the sub-segments, or null in case of parse failure */ static List parseSubSegments( boolean lenient, String segment ){ List subSegments = new ArrayList(); int xRef = 0; int anchor; char type; if(segment.startsWith("*") || segment.startsWith("!")) { anchor = 1; type = segment.charAt(0); } else { anchor = 0; type = '*'; } for( int i = anchor, limit = segment.length(); i < limit; i++ ){ char c = segment.charAt(i); if(xRef == 0) { if(c == ')') { return null; } else if (c == '(') { if(i == anchor) { xRef++; } else { return null; } } else if (c == '*' || c == '!') { String subSegment = parseSubSegment( lenient, type + segment.substring(anchor, i) ); if(subSegment == null) { return null; } else { subSegments.add(subSegment); type = c; anchor = i + 1; } } } else { if(c == ')') { xRef--; } else if (c == '(') { xRef++; } } } if(xRef == 0) { String subSegment = parseSubSegment( lenient, type + segment.substring(anchor) ); if(subSegment == null) { return null; } else { subSegments.add(subSegment); return subSegments; } } else { if(lenient) { return null; } else { throw new RuntimeServiceException( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.TRANSFORMATION_FAILURE, "Incomplete cross reference: Bot all parenthesis are closed", new BasicException.Parameter("segment",segment) ); } } } /** * Parse the cross reference's segments * * @param lenient tells whether exceptions are thrown or mapped to null return values * @param subSegment * * @return the cross reference's content */ List parseCrossReference( boolean lenient, String subSegment ){ return (subSegment.startsWith("*(") || subSegment.startsWith("!(")) && subSegment.endsWith(")") ? parseSegments(lenient, subSegment.substring(2, subSegment.length() - 1)) : null; } /** * Validate a single sub-segment with delimiter * * @param lenient tells whether exceptions are thrown or mapped to null return values * @param subSegment * * @return the subSegment, or null if it is not valid */ private static String parseSubSegment( boolean lenient, String subSegment ){ int limit = subSegment.length(); if(subSegment.startsWith("(", 1)) { if(subSegment.endsWith(")")) { String xri = subSegment.substring(2, limit -1 ); try { if (getExtensibleResourceIdentifier(true, xri) != null) { return subSegment; } else { new URI(xri); return subSegment; } } catch (URISyntaxException exception) { if(lenient) { return null; } else { throw new RuntimeServiceException( exception, BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.TRANSFORMATION_FAILURE, "Bad cross reference; It cntains neither an XRI nor an URI", new BasicException.Parameter("subSegment", subSegment), new BasicException.Parameter("xir", xri) ); } } } else { if(lenient) { return null; } else { throw new RuntimeServiceException( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.TRANSFORMATION_FAILURE, "Bad cross reference; The sub-segment starts with a parenthesis but does not end with one", new BasicException.Parameter("subSegment", subSegment) ); } } } else { int expectedHexadecimalDigits = 0; for( int pos = 1; pos < limit; pos++ ){ char c = subSegment.charAt(pos); if(expectedHexadecimalDigits > 0) { if(Character.digit(c, 16) < 0) { // // HEXDIG // if(lenient) { return null; } else { throw new RuntimeServiceException( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.TRANSFORMATION_FAILURE, "Bad escape sequence; The percent sign must be followed by two hexadecimal digits", new BasicException.Parameter("subSegment", subSegment), new BasicException.Parameter("position", pos), new BasicException.Parameter("digit", expectedHexadecimalDigits == 2 ? "first" : "second") ); } } else { expectedHexadecimalDigits--; } } else if (c == '%') { // // pct-encoded // expectedHexadecimalDigits = 2; } else if(!XRISegment.isPChar(c)) { // // NOT xri-pchar // if(lenient) { return null; } else { throw new RuntimeServiceException( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.TRANSFORMATION_FAILURE, "The sub-segment contains a character which is not a valid xri-pchar", new BasicException.Parameter("subSegment", subSegment), new BasicException.Parameter("position", pos), new BasicException.Parameter("character", c) ); } } } if(expectedHexadecimalDigits == 0) { return subSegment; } else { if(lenient) { return null; } else { throw new RuntimeServiceException( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.TRANSFORMATION_FAILURE, "Incomplete escape sequence; The percent sign must be followed by two hexadecimal digits", new BasicException.Parameter("subSegment", subSegment), new BasicException.Parameter("position", limit), new BasicException.Parameter("digit", expectedHexadecimalDigits == 2 ? "first" : "second") ); } } } } /** * Determines whether to segments match * * @param pathSegment * @param patternSegment * * @return true if the segments match */ private static boolean segmentMatchesPattern( Segment pathSegment, Segment patternSegment ){ List pathSubSegments = pathSegment.getSubSegments(); List patternSubSegments = patternSegment.getSubSegments(); int i = 0, limit = pathSubSegments.size(); for(String patternSubSegment : patternSubSegments) { if("*($..)".equals(patternSubSegment)) { return true; } if(i == limit) { return false; } String pathSubSegment = pathSubSegments.get(i++); if( !pathSubSegment.equals(patternSubSegment) && !"*($.)".equals(patternSubSegment) && !(patternSubSegment.startsWith("*($.") && pathSubSegment.startsWith(patternSubSegment.substring(4, patternSubSegment.length() - 1))) ) { return false; } } return i == limit; } /** * Determines whether to segments match * * @param candidate * @param pattern * * @return true if the segments match */ static boolean segmentMatchesPattern( String candidate, String pattern ){ Segment candidateSegment = new Segment(false, true, candidate); Segment patternSegment = new Segment(false, true, pattern); return candidateSegment.isValid() && patternSegment.isValid() && segmentMatchesPattern(candidateSegment, patternSegment); } /** * Determines whether the path corresponds to the pattern.
    *
  • ($.) matches any sub-segment *
  • ($.*‹prefix›) matches a re-assignable sub-segment with the given prefix *
  • ($.!‹prefix›) matches a persistent sub-segment with the given prefix *
  • ($..) matches any number of sub-segments, including 0
    * Note: This wildcard only allowed as last sub-segment in a segment. *
  • ($...) matches any number of segments, including 0
    * Note: This wildcard only allowed as last segment in a path. *
* @param pattern * * @return true if this path matches the pattern */ static boolean pathMatchesPattern( String path, String pattern ){ List pathSegments = getExtensibleResourceIdentifier(true, path); List patternSegments = getExtensibleResourceIdentifier(true, pattern); if(pathSegments == null || patternSegments == null) { return false; } int i = 0, limit = pathSegments.size(); for(Segment patternSegment : patternSegments) { if("($...)".equals(patternSegment.toString())) { return true; } if(i == limit || !segmentMatchesPattern(pathSegments.get(i++), patternSegment)) { return false; } } return i == limit; } //------------------------------------------------------------------------ // Class Segment //------------------------------------------------------------------------ /** * XRI Segment */ private static class Segment { /** * Constructor * * @param lenient tells whether the status is retrieved via isValid() or exceptions are thrown * * @param authority * @param xriSegment */ Segment( boolean lenient, boolean authority, String xriSegment ){ this.xriSegment = xriSegment; this.xriSubSegments = XRI_2Marshaller.parseSubSegments( lenient, authority ? xriSegment.substring(1) : xriSegment ); } final String xriSegment; final List xriSubSegments; @Override public String toString(){ return this.xriSegment; } List getSubSegments( ){ return this.xriSubSegments; } String getSubSegmentAt( int index ){ return this.xriSubSegments.get(index); } boolean isValid(){ return this.xriSubSegments != null; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy