/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can * select the license that you prefer but you may not use this file except in * compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet.ext.jaxrs.internal.core; import java.io.IOException; import java.util.List; import java.util.Map; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.PathSegment; import org.restlet.data.Reference; import org.restlet.engine.util.SystemUtils; import org.restlet.ext.jaxrs.internal.util.EncodeOrCheck; import org.restlet.ext.jaxrs.internal.util.Util; /** * The implementation of the JAX-RS interface {@link PathSegment} * * @author Stephan Koops */ public class PathSegmentImpl implements PathSegment { /** * @param matrParamString * The string to parse the matrix parameters * @param decoding * if true, than the keys and values are decoded, if false, than * not. * @param encodeAndCheckWhenNotDecode * If decode is false and encodeAndCheckWhenNotDecode is true, * than an IllegalArgumentException is thrown, when a parameter * contains illegal characters, if false nothing will checked. If * decode is true, this value is ignored. * @return Method is public for testing, otherwise it would be package * visible. */ public static MultivaluedMapImpl<String, String> parseMatrixParams( String matrParamString, boolean decoding) { final MultivaluedMapImpl<String, String> matrixParameters = new MultivaluedMapImpl<String, String>(); if (matrParamString == null) { return matrixParameters; } final String[] paramsEncSpl = matrParamString.split(";"); for (final String matrParamEnc : paramsEncSpl) { final int posEquSign = matrParamEnc.indexOf('='); String nameEnc; String valueEnc; if (posEquSign <= 0) { nameEnc = matrParamEnc; valueEnc = ""; } else { nameEnc = matrParamEnc.substring(0, posEquSign); valueEnc = matrParamEnc.substring(posEquSign + 1); } if ((nameEnc.length() == 0) && (valueEnc == null)) { continue; } String name; String value; if (decoding) { name = Reference.decode(nameEnc); value = Reference.decode(valueEnc); } else { name = nameEnc; value = valueEnc; } matrixParameters.add(name, value); } return matrixParameters; } private final boolean decode; /** * The matrix parameters. Encoded or decoded, depends on {@link #decode}. */ private volatile MultivaluedMap<String, String> matrixParameters; /** * the encoded matrix parameters, as given in constructor. */ private final String matrParamEncoded; /** encoded or decoded, depends on {@link #decode} */ private final String path; /** * @param segmentEnc * Segment with matrix parameter. The segment is encoded. * @param decode * true, if the path and the marix parameters should be decoded. * @param indexForErrMess * If the user adds more than one path segment with one call, you * can give the index for an error message here. Set -1, if none. * See * {@link EncodeOrCheck#checkForInvalidUriChars(String, int, String)} * @throws IllegalArgumentException * the segment is null, if decode and encode is both true */ public PathSegmentImpl(String segmentEnc, boolean decode, int indexForErrMess) throws IllegalArgumentException { if (segmentEnc == null) { if (indexForErrMess >= 0) { throw new IllegalArgumentException("The " + indexForErrMess + ". segment must not be null"); } throw new IllegalArgumentException("The segment must not be null"); } this.decode = decode; final int indexOfSemic = segmentEnc.indexOf(';'); String path; if (indexOfSemic >= 0) { path = segmentEnc.substring(0, indexOfSemic); this.matrParamEncoded = segmentEnc.substring(indexOfSemic + 1); } else { path = segmentEnc; this.matrParamEncoded = null; } this.path = decode ? Reference.decode(path) : path; } @Override public boolean equals(Object object) { if (this == object) { return true; } if (!(object instanceof PathSegmentImpl)) { return false; } final PathSegment other = (PathSegment) object; if (!getPath().equals(other.getPath())) { return false; } if (!getMatrixParameters().equals(other.getMatrixParameters())) { return false; } return true; } /** * Get a map of the decoded (or encoded?) matrix parameters associated with * the path segment * * @return the map of matrix parameters. Never returns null. */ public MultivaluedMap<String, String> getMatrixParameters() { if (this.matrixParameters == null) { this.matrixParameters = parseMatrixParams(this.matrParamEncoded, this.decode); } return this.matrixParameters; } /** * @return the path segment * @see PathSegment#getPath() */ public String getPath() { return this.path; } @Override public int hashCode() { return SystemUtils.hashCode(getPath(), getMatrixParameters()); } /** * Appends this PathSegment to the given Appendable * * @param stb * StringBuilder or other Appendable to append this PathSegment. * @param convertBraces * if true, all braces are converted, if false then not, see * {@link Util#append(Appendable, CharSequence, boolean)}. * @throws IOException * if the Appendable has a problem. */ public void toAppendable(Appendable stb, boolean convertBraces) throws IOException { Util.append(stb, this.path, convertBraces); final MultivaluedMap<String, String> matrixParams = getMatrixParameters(); for (final Map.Entry<String, List<String>> mpe : matrixParams .entrySet()) { for (final String value : mpe.getValue()) { stb.append(';'); Util.append(stb, mpe.getKey(), convertBraces); stb.append('='); Util.append(stb, value, convertBraces); } } } @Override public String toString() { final StringBuilder stb = new StringBuilder(); toStringBuilder(stb, false); return stb.toString(); } /** * Appends this PathSegment to the given StringBuilder * * @param stb * the StrinBuilder to append * @param convertBraces * if true, than conatined braces will be encoded, see * {@link Util#append(Appendable, CharSequence, boolean)}. */ public void toStringBuilder(StringBuilder stb, boolean convertBraces) { try { toAppendable(stb, convertBraces); } catch (IOException e) { throw new RuntimeException( "IOException in StringBuilder; that is normally not possible"); } } }