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

org.apache.jackrabbit.spi.commons.conversion.PathParser Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * 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.jackrabbit.spi.commons.conversion;

import org.apache.jackrabbit.spi.Path;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.PathFactory;
import org.apache.jackrabbit.spi.commons.name.PathBuilder;
import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;

import javax.jcr.NamespaceException;

/**
 * PathParser formats a {@link Path} using a
 * {@link NameResolver} and a {@link PathFactory}.
 */
public class PathParser {

    // constants for parser
    private static final int STATE_PREFIX_START = 0;
    private static final int STATE_PREFIX = 1;
    private static final int STATE_NAME_START = 2;
    private static final int STATE_NAME = 3;
    private static final int STATE_INDEX = 4;
    private static final int STATE_INDEX_END = 5;
    private static final int STATE_DOT = 6;
    private static final int STATE_DOTDOT = 7;
    private static final int STATE_IDENTIFIER = 8;
    private static final int STATE_URI = 9;
    private static final int STATE_URI_END = 10;

    private static final char EOF = (char) -1;

    /**
     * Parses jcrPath into a Path object using
     * resolver to convert prefixes into namespace URIs. If
     * resolver is null this method only checks the format of the
     * passed String and returns null.
     *
     * @param jcrPath the jcr path.
     * @param resolver the namespace resolver.
     * @param factory PathFactory to be used.
     * @return A path object.
     * @throws MalformedPathException If the jcrPath is malformed.
     * @throws IllegalNameException if any of the jcrNames is malformed.
     * @throws NamespaceException If an unresolvable prefix is encountered.
     */
    public static Path parse(String jcrPath, NameResolver resolver, PathFactory factory)
            throws MalformedPathException, IllegalNameException, NamespaceException {
        return parse(null, jcrPath, resolver, factory);
    }

    /**
     * Parses jcrPath into a Path object using
     * resolver to convert prefixes into namespace URIs. If the
     * specified jcrPath is an identifier based absolute path
     * beginning with an identifier segment the specified
     * IdentifierResolver will be used to resolve it to an
     * absolute path.
     * 

* If namResolver is null or if identifierResolver * is null and the path starts with an identifier segment, this * method only checks the format of the string and returns null. * * @param jcrPath the jcr path. * @param nameResolver the namespace resolver. * @param identifierResolver the resolver to validate any trailing identifier * segment and resolve to an absolute path. * @param factory * @return A path object. * @throws MalformedPathException If the jcrPath is malformed. * @throws IllegalNameException if any of the jcrNames is malformed. * @throws NamespaceException If an unresolvable prefix is encountered. * @since JCR 2.0 */ public static Path parse(String jcrPath, NameResolver nameResolver, IdentifierResolver identifierResolver, PathFactory factory) throws MalformedPathException, IllegalNameException, NamespaceException { return parse(null, jcrPath, nameResolver, identifierResolver, factory); } /** * Parses jcrPath into a Path object using * resolver to convert prefixes into namespace URIs. If the * specified jcrPath is an identifier based absolute path * beginning with an identifier segment the specified * IdentifierResolver will be used to resolve it to an * absolute path. *

* If namResolver is null or if identifierResolver * is null and the path starts with an identifier segment, this * method only checks the format of the string and returns null. * * @param jcrPath the jcr path. * @param nameResolver the namespace resolver. * @param identifierResolver the resolver to validate any trailing identifier * segment and resolve to an absolute path. * @param factory * @param normalizeIdentifier * @return A path object. * @throws MalformedPathException If the jcrPath is malformed. * @throws IllegalNameException if any of the jcrNames is malformed. * @throws NamespaceException If an unresolvable prefix is encountered. * @since JCR 2.0 */ public static Path parse(String jcrPath, NameResolver nameResolver, IdentifierResolver identifierResolver, PathFactory factory, boolean normalizeIdentifier) throws MalformedPathException, IllegalNameException, NamespaceException { return parse(null, jcrPath, nameResolver, identifierResolver, factory, normalizeIdentifier); } /** * Parses the given jcrPath and returns a Path. If * parent is not null, it is prepended to the * built path before it is returned. If resolver is * null, this method only checks the format of the string and * returns null. * * @param parent the parent path * @param jcrPath the JCR path * @param resolver the namespace resolver to get prefixes for namespace * URIs. * @param factory * @return the Path object. * @throws MalformedPathException If the jcrPath is malformed. * @throws IllegalNameException if any of the jcrNames is malformed. * @throws NamespaceException If an unresolvable prefix is encountered. */ public static Path parse(Path parent, String jcrPath, NameResolver resolver, PathFactory factory) throws MalformedPathException, IllegalNameException, NamespaceException { return parse(parent, jcrPath, resolver, null, factory); } /** * Parses the given jcrPath and returns a Path. If * parent is not null, it is prepended to the * built path before it is returned. If the specified jcrPath * is an identifier based absolute path beginning with an identifier segment * the given identifierResolver will be used to resolve it to an * absolute path. *

* If nameResolver is null or if identifierResolver * is null and the path starts with an identifier segment, this * method only checks the format of the string and returns null. * * @param parent the parent path. * @param jcrPath the jcr path. * @param nameResolver the namespace resolver. * @param identifierResolver the resolver to validate any trailing identifier * segment and resolve it to an absolute path. * @param factory The path factory. * @return the Path object. * @throws MalformedPathException * @throws IllegalNameException * @throws NamespaceException */ public static Path parse(Path parent, String jcrPath, NameResolver nameResolver, IdentifierResolver identifierResolver, PathFactory factory) throws MalformedPathException, IllegalNameException, NamespaceException { return parse(parent, jcrPath, nameResolver, identifierResolver, factory, true); } /** * Parses the given jcrPath and returns a Path. If * parent is not null, it is prepended to the * built path before it is returned. If the specified jcrPath * is an identifier based absolute path beginning with an identifier segment * the given identifierResolver will be used to resolve it to an * absolute path. *

* If nameResolver is null or if identifierResolver * is null and the path starts with an identifier segment, this * method only checks the format of the string and returns null. * * @param parent the parent path. * @param jcrPath the jcr path. * @param nameResolver the namespace resolver. * @param identifierResolver the resolver to validate any trailing identifier * segment and resolve it to an absolute path. * @param factory The path factory. * @param normalizeIdentifier * @return the Path object. * @throws MalformedPathException * @throws IllegalNameException * @throws NamespaceException */ private static Path parse(Path parent, String jcrPath, NameResolver nameResolver, IdentifierResolver identifierResolver, PathFactory factory, boolean normalizeIdentifier) throws MalformedPathException, IllegalNameException, NamespaceException { // check for length int len = jcrPath == null ? 0 : jcrPath.length(); // shortcut if (len == 1 && jcrPath.charAt(0) == '/') { return factory.getRootPath(); } if (len == 0) { throw new MalformedPathException("empty path"); } // check if absolute path PathBuilder builder = new PathBuilder(factory); int pos = 0; if (jcrPath.charAt(0) == '/') { if (parent != null) { throw new MalformedPathException("'" + jcrPath + "' is not a relative path."); } builder.addRoot(); pos++; } // add master if present if (parent != null) { builder.addAll(parent.getElements()); } // parse the path int state; if (jcrPath.charAt(0) == '[') { if (parent != null) { throw new MalformedPathException("'" + jcrPath + "' is not a relative path."); } state = STATE_IDENTIFIER; pos++; } else { state = STATE_PREFIX_START; } int lastPos = pos; String name = null; int index = Path.INDEX_UNDEFINED; boolean wasSlash = false; boolean checkFormat = (nameResolver == null); while (pos <= len) { char c = pos == len ? EOF : jcrPath.charAt(pos); char rawCharacter = c; pos++; // special check for whitespace if (c != ' ' && Character.isWhitespace(c)) { c = '\t'; } switch (c) { case '/': case EOF: if (state == STATE_PREFIX_START && c != EOF) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. double slash '//' not allowed."); } if (state == STATE_URI && c == EOF) { // this handles the case where URI state was entered but the end of the segment was reached (JCR-3562) state = STATE_URI_END; } if (state == STATE_PREFIX || state == STATE_NAME || state == STATE_INDEX_END || state == STATE_URI_END) { // eof pathelement if (name == null) { if (wasSlash) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path: Trailing slashes not allowed in prefixes and names."); } name = jcrPath.substring(lastPos, pos - 1); } // only add element if resolver not null. otherwise this // is just a check for valid format. if (checkFormat) { NameParser.checkFormat(name); } else { Name qName = nameResolver.getQName(name); builder.addLast(qName, index); } state = STATE_PREFIX_START; lastPos = pos; name = null; index = Path.INDEX_UNDEFINED; } else if (state == STATE_IDENTIFIER) { if (c == EOF) { // eof identifier reached if (jcrPath.charAt(pos - 2) != ']') { throw new MalformedPathException("'" + jcrPath + "' is not a valid path: Unterminated identifier segment."); } String identifier = jcrPath.substring(lastPos, pos - 2); if (checkFormat) { if (identifierResolver != null) { identifierResolver.checkFormat(identifier); } // else ignore. TODO: rather throw? } else if (identifierResolver == null) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path: Identifier segments are not supported."); } else if (normalizeIdentifier) { builder.addAll(identifierResolver.getPath(identifier).getElements()); } else { identifierResolver.checkFormat(identifier); builder.addLast(factory.createElement(identifier)); } state = STATE_PREFIX_START; lastPos = pos; } } else if (state == STATE_DOT) { builder.addLast(factory.getCurrentElement()); lastPos = pos; state = STATE_PREFIX_START; } else if (state == STATE_DOTDOT) { builder.addLast(factory.getParentElement()); lastPos = pos; state = STATE_PREFIX_START; } else if (state != STATE_URI && !(state == STATE_PREFIX_START && c == EOF)) { // ignore trailing slash throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not a valid name character."); } break; case '.': if (state == STATE_PREFIX_START) { state = STATE_DOT; } else if (state == STATE_DOT) { state = STATE_DOTDOT; } else if (state == STATE_DOTDOT) { state = STATE_PREFIX; } else if (state == STATE_INDEX_END) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not valid after index. '/' expected."); } break; case ':': if (state == STATE_PREFIX_START) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. Prefix must not be empty"); } else if (state == STATE_PREFIX) { if (wasSlash) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path: Trailing slashes not allowed in prefixes and names."); } state = STATE_NAME_START; // don't reset the lastPos/pos since prefix+name are passed together to the NameResolver } else if (state != STATE_IDENTIFIER && state != STATE_URI) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not valid name character"); } break; case '[': if (state == STATE_PREFIX || state == STATE_NAME) { if (wasSlash) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path: Trailing slashes not allowed in prefixes and names."); } state = STATE_INDEX; name = jcrPath.substring(lastPos, pos - 1); lastPos = pos; } else if (state != STATE_IDENTIFIER) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not a valid name character."); } break; case ']': if (state == STATE_INDEX) { try { index = Integer.parseInt(jcrPath.substring(lastPos, pos - 1)); } catch (NumberFormatException e) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. NumberFormatException in index: " + jcrPath.substring(lastPos, pos - 1)); } if (index < Path.INDEX_DEFAULT) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. Index number invalid: " + index); } state = STATE_INDEX_END; } else if (state != STATE_IDENTIFIER) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not a valid name character."); } break; case ' ': if (state == STATE_PREFIX_START || state == STATE_NAME_START) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not valid name start"); } else if (state == STATE_INDEX_END) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not valid after index. '/' expected."); } else if (state == STATE_DOT || state == STATE_DOTDOT) { state = STATE_PREFIX; } break; case '\t': if (state != STATE_IDENTIFIER) { String message = String.format("'%s' is not a valid path. Whitespace other than SP (U+0020) not a allowed in a name, but U+%04x was found at position %d.", jcrPath, (long) rawCharacter, pos - 1); throw new MalformedPathException(message); } case '*': case '|': if (state != STATE_IDENTIFIER) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not a valid name character."); } case '{': if (state == STATE_PREFIX_START && lastPos == pos-1) { // '{' marks the start of a uri enclosed in an expanded name // instead of the usual namespace prefix, if it is // located at the beginning of a new segment. state = STATE_URI; } else if (state == STATE_NAME_START || state == STATE_DOT || state == STATE_DOTDOT) { // otherwise it's part of the local name state = STATE_NAME; } break; case '}': if (state == STATE_URI) { state = STATE_URI_END; } break; default: if (state == STATE_PREFIX_START || state == STATE_DOT || state == STATE_DOTDOT) { state = STATE_PREFIX; } else if (state == STATE_NAME_START) { state = STATE_NAME; } else if (state == STATE_INDEX_END) { throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not valid after index. '/' expected."); } } wasSlash = c == ' '; } if (checkFormat) { // this was only for checking the format return null; } else { return builder.getPath(); } } /** * Check the format of the given jcr path. Note, the neither name nor * namespace validation (resolution of prefix to URI) is performed and * therefore will not be detected. * * @param jcrPath * @throws MalformedPathException If the jcrPath is malformed. */ public static void checkFormat(String jcrPath) throws MalformedPathException { try { // since no path is created -> use default factory parse(jcrPath, null, null, PathFactoryImpl.getInstance()); } catch (NamespaceException e) { // will never occur } catch (IllegalNameException e) { // will never occur } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy