org.apache.jackrabbit.test.api.PropertyUtil Maven / Gradle / Ivy
/*
* 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.test.api;
import javax.jcr.Property;
import javax.jcr.Session;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.ValueFormatException;
import javax.jcr.PropertyType;
import javax.jcr.NodeIterator;
import javax.jcr.PropertyIterator;
import javax.jcr.Value;
import javax.jcr.NamespaceException;
import javax.jcr.NamespaceRegistry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.IOException;
/**
* This class provides various utility methods that are used by the property
* test cases.
*/
public class PropertyUtil {
public static final String BASE_CHAR =
"\\u0041-\\u005A|\\u0061-\\u007A|\\u00C0-\\u00D6|\\u00D8-\\u00F6|\\u00F8-\\u00FF|\\u0100-\\u0131|\\u0134-\\u013E|" +
"\\u0141-\\u0148|\\u014A-\\u017E|\\u0180-\\u01C3|\\u01CD-\\u01F0|\\u01F4-\\u01F5|\\u01FA-\\u0217|\\u0250-\\u02A8|" +
"\\u02BB-\\u02C1|\\u0386|\\u0388-\\u038A|\\u038C|\\u038E-\\u03A1|\\u03A3-\\u03CE|\\u03D0-\\u03D6|\\u03DA|\\u03DC|" +
"\\u03DE|\\u03E0|\\u03E2-\\u03F3|\\u0401-\\u040C|\\u040E-\\u044F|\\u0451-\\u045C|\\u045E-\\u0481|\\u0490-\\u04C4|" +
"\\u04C7-\\u04C8|\\u04CB-\\u04CC|\\u04D0-\\u04EB|\\u04EE-\\u04F5|\\u04F8-\\u04F9|\\u0531-\\u0556|\\u0559|" +
"\\u0561-\\u0586|\\u05D0-\\u05EA|\\u05F0-\\u05F2|\\u0621-\\u063A|\\u0641-\\u064A|\\u0671-\\u06B7|\\u06BA-\\u06BE|" +
"\\u06C0-\\u06CE|\\u06D0-\\u06D3|\\u06D5|\\u06E5-\\u06E6|\\u0905-\\u0939|\\u093D|\\u0958-\\u0961|\\u0985-\\u098C|" +
"\\u098F-\\u0990|\\u0993-\\u09A8|\\u09AA-\\u09B0|\\u09B2|\\u09B6-\\u09B9|\\u09DC-\\u09DD|\\u09DF-\\u09E1|" +
"\\u09F0-\\u09F1|\\u0A05-\\u0A0A|\\u0A0F-\\u0A10|\\u0A13-\\u0A28|\\u0A2A-\\u0A30|\\u0A32-\\u0A33|\\u0A35-\\u0A36|" +
"\\u0A38-\\u0A39|\\u0A59-\\u0A5C|\\u0A5E|\\u0A72-\\u0A74|\\u0A85-\\u0A8B|\\u0A8D|\\u0A8F-\\u0A91|\\u0A93-\\u0AA8|" +
"\\u0AAA-\\u0AB0|\\u0AB2-\\u0AB3|\\u0AB5-\\u0AB9|\\u0ABD|\\u0AE0|\\u0B05-\\u0B0C|\\u0B0F-\\u0B10|\\u0B13-\\u0B28|" +
"\\u0B2A-\\u0B30|\\u0B32-\\u0B33|\\u0B36-\\u0B39|\\u0B3D|\\u0B5C-\\u0B5D|\\u0B5F-\\u0B61|\\u0B85-\\u0B8A|" +
"\\u0B8E-\\u0B90|\\u0B92-\\u0B95|\\u0B99-\\u0B9A|\\u0B9C|\\u0B9E-\\u0B9F|\\u0BA3-\\u0BA4|\\u0BA8-\\u0BAA|" +
"\\u0BAE-\\u0BB5|\\u0BB7-\\u0BB9|\\u0C05-\\u0C0C|\\u0C0E-\\u0C10|\\u0C12-\\u0C28|\\u0C2A-\\u0C33|\\u0C35-\\u0C39|" +
"\\u0C60-\\u0C61|\\u0C85-\\u0C8C|\\u0C8E-\\u0C90|\\u0C92-\\u0CA8|\\u0CAA-\\u0CB3|\\u0CB5-\\u0CB9|\\u0CDE|" +
"\u0CE0-\\u0CE1|\\u0D05-\\u0D0C|\\u0D0E-\\u0D10|\\u0D12-\\u0D28|\\u0D2A-\\u0D39|\\u0D60-\\u0D61|\\u0E01-\\u0E2E|" +
"\\u0E30|\\u0E32-\\u0E33|\\u0E40-\\u0E45|\\u0E81-\\u0E82|\\u0E84|\\u0E87-\\u0E88|\\u0E8A|\\u0E8D|\\u0E94-\\u0E97|" +
"\\u0E99-\\u0E9F|\\u0EA1-\\u0EA3|\\u0EA5|\\u0EA7|\\u0EAA-\\u0EAB|\\u0EAD-\\u0EAE|\\u0EB0|\\u0EB2-\\u0EB3|\\u0EBD|" +
"\\u0EC0-\\u0EC4|\\u0F40-\\u0F47|\\u0F49-\\u0F69|\\u10A0-\\u10C5|\\u10D0-\\u10F6|\\u1100|\\u1102-\\u1103|" +
"\\u1105-\\u1107|\\u1109|\\u110B-\\u110C|\\u110E-\\u1112|\\u113C|\\u113E|\\u1140|\\u114C|\\u114E|\\u1150|" +
"\\u1154-\\u1155|\\u1159|\\u115F-\\u1161|\\u1163|\\u1165|\\u1167|\\u1169|\\u116D-\\u116E|\\u1172-\\u1173|\\u1175|" +
"\\u119E|\\u11A8|\\u11AB|\\u11AE-\\u11AF|\\u11B7-\\u11B8|\\u11BA|\\u11BC-\\u11C2|\\u11EB|\\u11F0|\\u11F9|" +
"\\u1E00-\\u1E9B|\\u1EA0-\\u1EF9|\\u1F00-\\u1F15|\\u1F18-\\u1F1D|\\u1F20-\\u1F45|\\u1F48-\\u1F4D|\\u1F50-\\u1F57|" +
"\\u1F59|\\u1F5B|\\u1F5D|\\u1F5F-\\u1F7D|\\u1F80-\\u1FB4|\\u1FB6-\\u1FBC|\\u1FBE|\\u1FC2-\\u1FC4|\\u1FC6-\\u1FCC|" +
"\\u1FD0-\\u1FD3|\\u1FD6-\\u1FDB|\\u1FE0-\\u1FEC|\\u1FF2-\\u1FF4|\\u1FF6-\\u1FFC|\\u2126|\\u212A-\\u212B|\\u212E|" +
"\\u2180-\\u2182|\\u3041-\\u3094|\\u30A1-\\u30FA|\\u3105-\\u312C|\\uAC00-\\uD7A3";
public static final String IDEOGRAPHIC = "\\u4E00-\\u9FA5|\\u3007|\\u3021-\\u3029";
public static final String COMBINING_CHAR =
"\\u0300-\\u0345|\\u0360-\\u0361|\\u0483-\\u0486|\\u0591-\\u05A1|\\u05A3-\\u05B9|\\u05BB-\\u05BD|\\u05BF|" +
"\\u05C1-\\u05C2|\\u05C4|\\u064B-\\u0652|\\u0670|\\u06D6-\\u06DC|\\u06DD-\\u06DF|\\u06E0-\\u06E4|\\u06E7-\\u06E8|" +
"\\u06EA-\\u06ED|\\u0901-\\u0903|\\u093C|\\u093E-\\u094C|\\u094D|\\u0951-\\u0954|\\u0962-\\u0963|\\u0981-\\u0983|" +
"\\u09BC|\\u09BE|\\u09BF|\\u09C0-\\u09C4|\\u09C7-\\u09C8|\\u09CB-\\u09CD|\\u09D7|\\u09E2-\\u09E3|\\u0A02|\\u0A3C|" +
"\\u0A3E|\\u0A3F|\\u0A40-\\u0A42|\\u0A47-\\u0A48|\\u0A4B-\\u0A4D|\\u0A70-\\u0A71|\\u0A81-\\u0A83|\\u0ABC|" +
"\\u0ABE-\\u0AC5|\\u0AC7-\\u0AC9|\\u0ACB-\\u0ACD|\\u0B01-\\u0B03|\\u0B3C|\\u0B3E-\\u0B43|\\u0B47-\\u0B48|" +
"\\u0B4B-\\u0B4D|\\u0B56-\\u0B57|\\u0B82-\\u0B83|\\u0BBE-\\u0BC2|\\u0BC6-\\u0BC8|\\u0BCA-\\u0BCD|\\u0BD7|" +
"\\u0C01-\\u0C03|\\u0C3E-\\u0C44|\\u0C46-\\u0C48|\\u0C4A-\\u0C4D|\\u0C55-\\u0C56|\\u0C82-\\u0C83|\\u0CBE-\\u0CC4|" +
"\\u0CC6-\\u0CC8|\\u0CCA-\\u0CCD|\\u0CD5-\\u0CD6|\\u0D02-\\u0D03|\\u0D3E-\\u0D43|\\u0D46-\\u0D48|\\u0D4A-\\u0D4D|" +
"\\u0D57|\\u0E31|\\u0E34-\\u0E3A|\\u0E47-\\u0E4E|\\u0EB1|\\u0EB4-\\u0EB9|\\u0EBB-\\u0EBC|\\u0EC8-\\u0ECD|" +
"\\u0F18-\\u0F19|\\u0F35|\\u0F37|\\u0F39|\\u0F3E|\\u0F3F|\\u0F71-\\u0F84|\\u0F86-\\u0F8B|\\u0F90-\\u0F95|\\u0F97|" +
"\\u0F99-\\u0FAD|\\u0FB1-\\u0FB7|\\u0FB9|\\u20D0-\\u20DC|\\u20E1|\\u302A-\\u302F|\\u3099|\\u309A";
public static final String DIGIT =
"\\u0030-\\u0039|\\u0660-\\u0669|\\u06F0-\\u06F9|\\u0966-\\u096F|\\u09E6-\\u09EF|\\u0A66-\\u0A6F|\\u0AE6-\\u0AEF|" +
"\\u0B66-\\u0B6F|\\u0BE7-\\u0BEF|\\u0C66-\\u0C6F|\\u0CE6-\\u0CEF|\\u0D66-\\u0D6F|\\u0E50-\\u0E59|\\u0ED0-\\u0ED9|" +
"\\u0F20-\\u0F29";
public static final String EXTENDER =
"\\u00B7|\\u02D0|\\u02D1|\\u0387|\\u0640|\\u0E46|\\u0EC6|\\u3005|\\u3031-\\u3035|\\u309D-\\u309E|\\u30FC-\\u30FE";
/*
name's prefix must be a valid xml name:
http://www.w3.org/TR/REC-xml-names
[4] NCName ::= (Letter | '_') (NCNameChar)* // An XML Name, minus the ":"
[5] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender
[84] Letter ::= BaseChar | Ideographic
*/
public static final String LETTER = BASE_CHAR + "|" + IDEOGRAPHIC;
public static final String NC_NAME =
"[" + LETTER + "|_]" +
"[" + LETTER + "|" + DIGIT + "|.|\\-|_|" + COMBINING_CHAR + "|" + EXTENDER +"]*";
public static final String SIMPLENAME_CHAR =
"[^/:\\[\\]\\*'\"\\s]";
public static final String PATTERNSTRING_NAME =
"((" + NC_NAME + "):)?" + // prefix
SIMPLENAME_CHAR + "([" + SIMPLENAME_CHAR + "| ]*" + SIMPLENAME_CHAR +")?";
public static final Pattern PATTERN_NAME = Pattern.compile(PATTERNSTRING_NAME);
public static final String PATTERNSTRING_PATH_ELEMENT =
PATTERNSTRING_NAME +
"(\\[[1-9]\\d*\\])?";
public static final String PATTERNSTRING_PATH_WITHOUT_LAST_SLASH =
"(\\./|\\.\\./|/)?" +
"(" + PropertyUtil.PATTERNSTRING_PATH_ELEMENT + "/)*" +
PropertyUtil.PATTERNSTRING_PATH_ELEMENT;
public static final String PATTERNSTRING_PATH =
PATTERNSTRING_PATH_WITHOUT_LAST_SLASH + "/?";
public static final Pattern PATTERN_PATH = Pattern.compile(PATTERNSTRING_PATH);
public static final String PATTERNSTRING_DATE =
"[0-9][0-9][0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]||2[0-9]|3[01])" +
"T([0-1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9].[0-9][0-9][0-9]" +
"(Z|[+-]([0-1][0-9]|2[0-3]):[0-5][0-9])";
public static final Pattern PATTERN_DATE = Pattern.compile(PATTERNSTRING_DATE);
/**
* Private constructor to disable instantiation.
*/
private PropertyUtil() {
}
/**
* Traverses a tree below a given node searching for a property with a given
* type
*
* @param node the node to start traverse
* @param type the property type to search for
* @param multiple whether the property should be multivalued (null
: does not matter)
* @return the property found or null if no property is found
*/
public static Property searchProp(Session session, Node node, int type, Boolean multiple)
throws RepositoryException, ValueFormatException {
Property prop = null;
int propType = PropertyType.UNDEFINED;
if (prop == null) {
for (PropertyIterator props = node.getProperties(); props.hasNext();) {
Property property = props.nextProperty();
propType = property.getType();
if (propType == type && (multiple == null || multiple.booleanValue() == property.getDefinition().isMultiple())) {
prop = property;
break;
}
}
}
if (prop == null) {
for (NodeIterator nodes = node.getNodes(); nodes.hasNext();) {
Node n = nodes.nextNode();
prop = searchProp(session, n, type, multiple);
if (prop != null) {
break;
}
}
}
return prop;
}
/**
* Returns the value of a property. If prop
is multi valued
* this method returns the first value.
*
* @param prop the property from which to return the value.
* @return the value of the property.
*/
public static Value getValue(Property prop) throws RepositoryException {
Value val;
if (prop.getDefinition().isMultiple()) {
Value[] vals = prop.getValues();
if (vals.length > 0) {
val = vals[0];
} else {
val = null;
}
} else {
val = prop.getValue();
}
return val;
}
/**
* checks if the given name follows the NAME syntax rules and if a present
* prefix is mapped to a registered namespace
*
* @param name the string to test
*/
public static boolean checkNameFormat(String name, Session session) throws RepositoryException {
if (name == null || name.length() == 0) {
return false;
} else {
NamespaceRegistry nsr = session.getWorkspace().getNamespaceRegistry();
boolean prefixOk = true;
// validate name element
Matcher matcher = PATTERN_NAME.matcher(name);
// validate namespace prefixes if present
String[] split = name.split(":");
if (split.length > 1) {
String prefix = split[0];
try {
nsr.getURI(prefix);
} catch (NamespaceException nse) {
prefixOk = false;
}
}
return matcher.matches() && prefixOk;
}
}
/**
* Checks if the given path follows the path syntax rules.
*
* @param jcrPath the string to test
*/
public static boolean checkPathFormat(String jcrPath, Session session) throws RepositoryException {
if (jcrPath == null || jcrPath.length() == 0) {
return false;
} else if (jcrPath.equals("/")) {
return true;
} else {
NamespaceRegistry nsr = session.getWorkspace().getNamespaceRegistry();
boolean match = false;
boolean prefixOk = true;
// split path into path elements and validate each of them
String[] elems = jcrPath.split("/", -1);
for (int i = jcrPath.startsWith("/") ? 1 : 0; i < elems.length; i++) {
// validate path element
String elem = elems[i];
Matcher matcher = PATTERN_PATH.matcher(elem);
match = matcher.matches();
if (!match) {
break;
}
// validate namespace prefixes if present
String[] split = elem.split(":");
if (split.length > 1) {
String prefix = split[0];
try {
nsr.getURI(prefix);
} catch (NamespaceException nse) {
prefixOk = false;
break;
}
}
}
return match && prefixOk;
}
}
/**
* Checks if the String is a valid date in string format.
*
* @param str the string to test.
* @return true
if str
is a valid date format.
*/
public static boolean isDateFormat(String str) {
return PATTERN_DATE.matcher(str).matches();
}
/**
* Counts the number of bytes of a Binary value.
*
* @param val the binary value.
* @return the number of bytes or -1 in case of any exception
*/
public static long countBytes(Value val) {
int length = 0;
InputStream in = null;
try {
in = val.getStream();
BufferedInputStream bin = new BufferedInputStream(in);
while (bin.read() != -1) {
length++;
}
bin.close();
} catch (Exception e) {
length = -1;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignore) {}
}
}
return length;
}
/**
* Helper method to test the type received with Value.getType() and
* Property.getType() .
*/
public static boolean checkGetType(Property prop, int propType) throws RepositoryException {
Value val = getValue(prop);
boolean samePropType = (val.getType() == propType);
int requiredType = prop.getDefinition().getRequiredType();
if (requiredType != PropertyType.UNDEFINED) {
samePropType = (val.getType() == requiredType);
}
return samePropType;
}
/**
* Helper method to compare the equality of two values for equality with the
* fulfilling of the equality conditions. These conditions for the values
* are to have the same type and the same string representation.
*
* @param val1 first value
* @param val2 second value
* @return true if the equals method is equivalent to the normative
* definition of value equality, false in the other case.
*/
public static boolean equalValues(Value val1, Value val2) throws RepositoryException {
boolean isEqual = val1.equals(val2);
boolean conditions = false;
try {
conditions = (val1.getType() == val2.getType())
&& val1.getString().equals(val2.getString());
} catch (ValueFormatException vfe) {
return false;
}
return (isEqual == conditions);
}
/**
* Helper method to assure that no property with a null value exist.
*
* @param node the node to start the search from.
* @return true
if a null value property is found;
* false
in the other case.
*/
public static boolean nullValues(Node node) throws RepositoryException {
boolean nullValue = false;
for (PropertyIterator props = node.getProperties(); props.hasNext();) {
Property property = props.nextProperty();
if (!property.getDefinition().isMultiple()) {
nullValue = (property.getValue() == null);
if (nullValue) {
break;
}
}
}
if (!nullValue) {
for (NodeIterator nodes = node.getNodes(); nodes.hasNext();) {
Node n = nodes.nextNode();
nullValue = nullValues(n);
}
}
return nullValue;
}
/**
* Helper method to find a multivalue property.
*
* @param node the node to start the search from.
* @return a multivalue property or null if not found any.
*/
public static Property searchMultivalProp(Node node) throws RepositoryException {
Property multiVal = null;
for (PropertyIterator props = node.getProperties(); props.hasNext();) {
Property property = props.nextProperty();
if (property.getDefinition().isMultiple()) {
multiVal = property;
break;
}
}
if (multiVal == null) {
for (NodeIterator nodes = node.getNodes(); nodes.hasNext();) {
Node n = nodes.nextNode();
multiVal = searchMultivalProp(n);
if (multiVal != null) {
break;
}
}
}
return multiVal;
}
/**
* Helper method to find a multivalue property of a given type.
*
* @param node the node to start the search from.
* @param type the property type.
* @return a multivalue property or null if not found any.
*/
public static Property searchMultivalProp(Node node, int type) throws RepositoryException {
Property multiVal = null;
for (PropertyIterator props = node.getProperties(); props.hasNext();) {
Property property = props.nextProperty();
if (property.getDefinition().isMultiple()
&& property.getType() == type) {
multiVal = property;
break;
}
}
if (multiVal == null) {
for (NodeIterator nodes = node.getNodes(); nodes.hasNext();) {
Node n = nodes.nextNode();
multiVal = searchMultivalProp(n, type);
if (multiVal != null) {
break;
}
}
}
return multiVal;
}
/**
* Retrieve a single valued property from the given node.
*
* @param node
* @return the property found or null if no property is found.
*/
public static Property searchSingleValuedProperty(Node node)
throws RepositoryException, ValueFormatException {
PropertyIterator props = node.getProperties();
while (props.hasNext()) {
Property p = props.nextProperty();
if (!p.getDefinition().isMultiple()) {
return p;
}
}
// should never get here, since every Node must provide the jcr:primaryType
// property, which is single valued.
return null;
}
}