Package org.restlet.ext.jaxrs.internal.core

Source Code of org.restlet.ext.jaxrs.internal.core.CallContext$MatrixParamEncIter

/**
* Copyright 2005-2011 Noelios Technologies.
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: LGPL 3.0 or LGPL 2.1 or CDDL 1.0 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 LGPL 3.0 license at
* http://www.opensource.org/licenses/lgpl-3.0.html
*
* You can obtain a copy of the LGPL 2.1 license at
* http://www.opensource.org/licenses/lgpl-2.1.php
*
* You can obtain a copy of the CDDL 1.0 license at
* http://www.opensource.org/licenses/cddl1.php
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* 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://www.noelios.com/products/restlet-engine
*
* Restlet is a registered trademark of Noelios Technologies.
*/

package org.restlet.ext.jaxrs.internal.core;

import java.net.URI;
import java.net.URISyntaxException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ws.rs.MatrixParam;
import javax.ws.rs.PathParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.core.Variant;

import org.restlet.Application;
import org.restlet.Request;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.CharacterSet;
import org.restlet.data.Dimension;
import org.restlet.data.Form;
import org.restlet.data.Language;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.data.Tag;
import org.restlet.ext.jaxrs.ExtendedUriBuilder;
import org.restlet.ext.jaxrs.internal.todo.NotYetImplementedException;
import org.restlet.ext.jaxrs.internal.util.Converter;
import org.restlet.ext.jaxrs.internal.util.EmptyIterator;
import org.restlet.ext.jaxrs.internal.util.SecurityUtil;
import org.restlet.ext.jaxrs.internal.util.SortedMetadata;
import org.restlet.ext.jaxrs.internal.util.Util;
import org.restlet.representation.Representation;
import org.restlet.security.Role;

/**
* Contains all request specific data of the interfaces injectable for @
* {@link Context}. Implementation of the JAX-RS interfaces {@link HttpHeaders},
* {@link UriInfo}, {@link javax.ws.rs.core.Request} and {@link SecurityContext}
* .<br>
* This class is not required to be thread safe, because it is only used for one
* client request in one thread at the same time.
*
* @author Stephan Koops
*/
public class CallContext implements javax.ws.rs.core.Request, HttpHeaders,
        SecurityContext {

    /**
     * Iterator to return the values for a matrix parameter.
     *
     * @author Stephan Koops
     */
    private static class MatrixParamEncIter implements Iterator<String> {

        /** Iterates over the matrix parameters of one path segment */
        private Iterator<Map.Entry<String, List<String>>> matrixParamIter;

        private final String mpName;

        private Iterator<String> mpValueIter;

        private String nextMpValue;

        private final Iterator<PathSegment> pathSegmentIter;

        MatrixParamEncIter(String mpName, List<PathSegment> pathSegmentsEnc) {
            this.pathSegmentIter = pathSegmentsEnc.iterator();
            this.mpName = mpName;
        }

        /**
         * @see java.util.Iterator#hasNext()
         */
        public boolean hasNext() {
            if (this.nextMpValue != null) {
                return true;
            }
            while ((this.mpValueIter != null) && (this.mpValueIter.hasNext())) {
                this.nextMpValue = this.mpValueIter.next();
                return true;
            }
            while ((this.matrixParamIter != null)
                    && (matrixParamIter.hasNext())) {
                final Map.Entry<String, List<String>> entry = matrixParamIter
                        .next();
                if (entry.getKey().equals(this.mpName)) {
                    this.mpValueIter = entry.getValue().iterator();
                    return hasNext();
                }
            }
            while (this.pathSegmentIter.hasNext()) {
                this.matrixParamIter = this.pathSegmentIter.next()
                        .getMatrixParameters().entrySet().iterator();
                return hasNext();
            }
            return false;
        }

        /**
         * @see java.util.Iterator#next()
         */
        public String next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            final String nextMpValue = this.nextMpValue;
            this.nextMpValue = null;
            return nextMpValue;
        }

        /**
         * @see java.util.Iterator#remove()
         */
        public void remove() {
            throw new UnsupportedOperationException("unmodifiable");
        }
    }

    private static final int STATUS_PREC_FAILED = Status.CLIENT_ERROR_PRECONDITION_FAILED
            .getCode();

    private static final Logger unexpectedLogger = org.restlet.Context
            .getCurrentLogger();

    /**
     * the unmodifiable List of accepted languages. Lazy initialization by
     * getter.
     *
     * @see #getAcceptableLanguages()
     */
    private List<Locale> acceptedLanguages;

    /**
     * the unmodifiable List of accepted {@link MediaType}s. Lazy initialization
     * by getter.
     *
     * @see #getAcceptableMediaTypes()
     */
    private List<MediaType> acceptedMediaTypes;

    private final SortedMetadata<org.restlet.data.MediaType> accMediaTypes;

    /** contains the current value of the ancestor resources */
    private final LinkedList<Object> matchedResources = new LinkedList<Object>();

    /** contains the current value of the ancestor resource URIs */
    private final LinkedList<String> matchedURIs = new LinkedList<String>();

    private String baseUri;

    private Map<String, Cookie> cookies;

    private Locale language;

    private MediaType mediaType;

    private MultivaluedMap<String, String> pathParametersDecoded;

    /** is null, if no templateParameters given on creation */
    private MultivaluedMap<String, String> pathParametersEncoded;

    private List<PathSegment> pathSegmentsDecoded = null;

    private List<PathSegment> pathSegmentsEncoded = null;

    private MultivaluedMap<String, String> queryParametersDecoded;

    private MultivaluedMap<String, String> queryParametersEncoded;

    private boolean readOnly = false;

    private final Reference referenceCut;

    private final Reference referenceOriginal;

    private final Request request;

    private UnmodifiableMultivaluedMap<String, String> requestHeaders;

    private final org.restlet.Response response;

    /**
     *
     * @param request
     *            The Restlet request to wrap. Must not be null.
     * @param response
     *            The Restlet response
     * @param roleChecker
     *            Optional, can be null, see {@link RoleChecker}.
     */
    public CallContext(Request request, org.restlet.Response response) {
        if (request == null) {
            throw new IllegalArgumentException(
                    "The Restlet Request must not be null");
        }
        if (response == null) {
            throw new IllegalArgumentException(
                    "The Restlet Response must not be null");
        }
        final Reference referenceCut = request.getResourceRef();
        if (referenceCut == null) {
            throw new IllegalArgumentException(
                    "The request reference must not be null");
        }
        if (referenceCut.getBaseRef() == null) {
            throw new IllegalArgumentException(
                    "The request reference must contains a baseRef");
        }
        final Reference referenceOriginal = request.getOriginalRef();
        if (referenceOriginal == null) {
            throw new IllegalArgumentException(
                    "The request.originalRef must not be null");
        }
        final Reference appRootRef = request.getRootRef();
        if (appRootRef == null) {
            throw new IllegalArgumentException(
                    "The root reference of the request must not be null");
        }
        referenceOriginal.setBaseRef(appRootRef);
        this.referenceCut = referenceCut;
        this.referenceOriginal = referenceOriginal;
        this.readOnly = false;
        this.request = request;
        this.response = response;
        this.accMediaTypes = SortedMetadata.getForMediaTypes(request
                .getClientInfo().getAcceptedMediaTypes());
    }

    /**
     * also useable after {@link #setReadOnly()}
     *
     * @param resourceObject
     * @param newUriPart
     * @throws URISyntaxException
     * @see UriInfo#getMatchedResources()
     * @see UriInfo#getMatchedURIs()
     */
    public void addForMatched(Object resourceObject, String newUriPart) {
        if (resourceObject == null) {
            throw new IllegalArgumentException(
                    "The resource object must not be null");
        }
        if (newUriPart == null) {
            throw new IllegalArgumentException(
                    "The new URI part must not be null");
        }

        final StringBuilder newUri;
        if (this.matchedURIs.isEmpty())
            newUri = new StringBuilder();
        else
            newUri = new StringBuilder(this.matchedURIs.getFirst());
        if (newUriPart.length() == 0 || newUriPart.charAt(0) != '/') {
            newUri.append('/');
        }
        newUri.append(newUriPart);
        this.matchedResources.addFirst(resourceObject);
        this.matchedURIs.addFirst(newUri.toString());
    }

    /**
     * @param varName
     * @param varValue
     */
    public void addPathParamsEnc(String varName, String varValue) {
        checkChangeable();
        interalGetPathParamsEncoded().add(varName, varValue);
    }

    /**
     * Checks, if this object is changeable. If not, a
     * {@link IllegalStateException} is thrown.
     *
     * @throws IllegalStateException
     */
    protected void checkChangeable() throws IllegalStateException {
        if (!isChangeable()) {
            throw new IllegalStateException(
                    "The CallContext is no longer changeable");
        }
    }

    /**
     * Creates an unmodifiable List of {@link PathSegment}s.
     *
     * @param decode
     *            indicates, if the values should be decoded or not
     * @return
     */
    private List<PathSegment> createPathSegments(boolean decode) {
        List<String> segmentsEnc;
        segmentsEnc = this.referenceOriginal.getRelativeRef().getSegments();
        final int l = segmentsEnc.size();
        final List<PathSegment> pathSegments = new ArrayList<PathSegment>(l);
        for (int i = 0; i < l; i++) {
            final String segmentEnc = segmentsEnc.get(i);
            pathSegments.add(new PathSegmentImpl(segmentEnc, decode, i));
        }
        return Collections.unmodifiableList(pathSegments);
    }

    /**
     * @param ref
     * @return
     * @throws IllegalArgumentException
     */
    private UriBuilder createUriBuilder(Reference ref) {
        // NICE what happens, if the Reference is invalid for the UriBuilder?
        UriBuilder b = new UriBuilderImpl();
        return fillUriBuilder(ref, b);
    }

    /**
     * @param ref
     * @param b
     * @return
     * @throws IllegalArgumentException
     */
    private UriBuilder fillUriBuilder(Reference ref, final UriBuilder b)
            throws IllegalArgumentException {
        b.scheme(ref.getScheme(false));
        b.userInfo(ref.getUserInfo(false));
        b.host(ref.getHostDomain(false));
        b.port(ref.getHostPort());
        b.path(ref.getPath(false));
        b.replaceQuery(ref.getQuery(false));
        b.fragment(ref.getFragment(false));
        return b;
    }

    private ExtendedUriBuilder createExtendedUriBuilder(Reference ref) {
        ExtendedUriBuilder b = new ExtendedUriBuilder();
        fillUriBuilder(ref, b);
        String extension = ref.getExtensions();
        b.extension(extension);
        return b;
    }

    @Override
    public boolean equals(Object anotherObject) {
        if (this == anotherObject) {
            return true;
        }
        if (!(anotherObject instanceof UriInfo)) {
            return false;
        }
        final UriInfo other = (UriInfo) anotherObject;
        if (!getBaseUri().equals(other.getBaseUri())) {
            return false;
        }
        if (!this.getPathSegments().equals(other.getPathSegments())) {
            return false;
        }
        if (!Util.equals(this.getPathParameters(), other.getPathParameters())) {
            return false;
        }
        return true;
    }

    /**
     * Evaluate request preconditions based on the passed in value.
     *
     * @param lastModified
     *            a date that specifies the modification date of the resource
     * @return null if the preconditions are met or a ResponseBuilder set with
     *         the appropriate status if the preconditions are not met.
     * @throws java.lang.IllegalArgumentException
     *             if lastModified is null
     * @throws java.lang.IllegalStateException
     *             if called outside the scope of a request
     * @see #evaluatePreconditions(Date, EntityTag)
     * @see javax.ws.rs.core.Request#evaluatePreconditions(java.util.Date)
     */
    public ResponseBuilder evaluatePreconditions(Date lastModified) {
        if (lastModified == null) {
            throw new IllegalArgumentException(
                    "The last modification date must not be null");
        }
        return evaluatePreconditionsInternal(lastModified, null);
    }

    /**
     * Evaluates the preconditions of the current request against the given last
     * modified date and / or the given entity tag. This method does not check,
     * if the arguments are not null.
     *
     * @param lastModified
     * @param entityTag
     * @return
     * @see Request#evaluateConditions(Tag, Date)
     */
    private ResponseBuilder evaluatePreconditionsInternal(
            final Date lastModified, final EntityTag entityTag) {
        Status status = this.request.getConditions().getStatus(
                this.request.getMethod(), true,
                Converter.toRestletTag(entityTag), lastModified);

        if (status == null)
            return null;
        if (status.equals(Status.REDIRECTION_NOT_MODIFIED)) {
            final ResponseBuilder rb = Response.notModified();
            rb.lastModified(lastModified);
            rb.tag(entityTag);
            return rb;
        }

        return Response.status(STATUS_PREC_FAILED);
    }

    /**
     * Evaluate request preconditions based on the passed in value.
     *
     * @param lastModified
     *            a date that specifies the modification date of the resource
     * @param eTag
     *            an ETag for the current state of the resource
     * @return null if the preconditions are met or a ResponseBuilder set with
     *         the appropriate status if the preconditions are not met. A
     *         returned ResponseBuilder will include an ETag header set with the
     *         value of eTag.
     * @throws java.lang.IllegalArgumentException
     *             if lastModified or eTag is null
     * @throws java.lang.IllegalStateException
     *             if called outside the scope of a request
     *
     * @see javax.ws.rs.core.Request#evaluatePreconditions(java.util.Date,
     *      javax.ws.rs.core.EntityTag)
     * @see <a href="http://tools.ietf.org/html/rfc2616#section-10.3.5">RFC
     *      2616, section 10.3.5: Status 304: Not Modified</a>
     * @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.13">RFC
     *      2616, section 10.4.13: Status 412: Precondition Failed</a>
     * @see <a href="http://tools.ietf.org/html/rfc2616#section-13.3">RFC 2616,
     *      section 13.3: (Caching) Validation Model</a>
     * @see <a href="http://tools.ietf.org/html/rfc2616#section-14.24">RFC 2616,
     *      section 14.24: Header "If-Match"</a>
     * @see <a href="http://tools.ietf.org/html/rfc2616#section-14.25">RFC 2616,
     *      section 14.25: Header "If-Modified-Since"</a>
     * @see <a href="http://tools.ietf.org/html/rfc2616#section-14.26">RFC 2616,
     *      section 14.26: Header "If-None-Match"</a>
     * @see <a href="http://tools.ietf.org/html/rfc2616#section-14.28">RFC 2616,
     *      section 14.28: Header "If-Unmodified-Since"</a>
     */
    public ResponseBuilder evaluatePreconditions(Date lastModified,
            EntityTag entityTag) {
        if (lastModified == null) {
            throw new IllegalArgumentException(
                    "The last modification date must not be null");
        }
        if (entityTag == null) {
            throw new IllegalArgumentException(
                    "The entity tag must not be null");
        }
        return evaluatePreconditionsInternal(lastModified, entityTag);
    }

    /**
     * Evaluate request preconditions based on the passed in value.
     *
     * @param eTag
     *            an ETag for the current state of the resource
     * @return null if the preconditions are met or a ResponseBuilder set with
     *         the appropriate status if the preconditions are not met. A
     *         returned ResponseBuilder will include an ETag header set with the
     *         value of eTag.
     * @throws java.lang.IllegalArgumentException
     *             if eTag is null
     * @throws java.lang.IllegalStateException
     *             if called outside the scope of a request
     * @see #evaluatePreconditions(Date, EntityTag)
     * @see javax.ws.rs.core.Request#evaluatePreconditions(javax.ws.rs.core.EntityTag)
     */
    public ResponseBuilder evaluatePreconditions(EntityTag entityTag) {
        if (entityTag == null) {
            throw new IllegalArgumentException(
                    "The entity tag must not be null");
        }
        return evaluatePreconditionsInternal(null, entityTag);
    }

    /**
     * Get the absolute path of the request. This includes everything preceding
     * the path (host, port etc) but excludes query parameters and fragment.
     * This is a shortcut for
     * <code>uriInfo.getBase().resolve(uriInfo.getPath()).</code>
     *
     * @return the absolute path of the request
     * @see UriInfo#getAbsolutePath()
     */
    public URI getAbsolutePath() {
        try {
            return new URI(this.referenceOriginal.toString(false, false));
        } catch (URISyntaxException e) {
            throw wrapUriSyntaxExc(e, unexpectedLogger, "Could not create URI");
        }
    }

    /**
     * Get the absolute path of the request in the form of a UriBuilder. This
     * includes everything preceding the path (host, port etc) but excludes
     * query parameters and fragment.
     *
     * @return a UriBuilder initialized with the absolute path of the request.
     * @see UriInfo#getAbsolutePathBuilder()
     */
    public UriBuilder getAbsolutePathBuilder() {
        return createUriBuilder(this.referenceOriginal);
    }

    ExtendedUriBuilder getAbsolutePathBuilderExtended() {
        return createExtendedUriBuilder(this.referenceOriginal);
    }

    /**
     * @see javax.ws.rs.core.HttpHeaders#getAcceptableLanguages()
     */
    public List<Locale> getAcceptableLanguages() {
        if (this.acceptedLanguages == null) {
            final SortedMetadata<Language> accLangages = SortedMetadata
                    .getForLanguages(this.request.getClientInfo()
                            .getAcceptedLanguages());
            final List<Locale> accLangs = new ArrayList<Locale>();
            for (final Language language : accLangages) {
                accLangs.add(Converter.toLocale(language));
            }
            this.acceptedLanguages = Collections.unmodifiableList(accLangs);
        }
        return this.acceptedLanguages;
    }

    /**
     * For use from JAX-RS interface.
     *
     * @see HttpHeaders#getAcceptableMediaTypes()
     */
    public List<MediaType> getAcceptableMediaTypes() {
        if (this.acceptedMediaTypes == null) {
            final List<MediaType> accMediaTypes = new ArrayList<MediaType>();
            for (final org.restlet.data.MediaType mediaType : this.accMediaTypes) {
                accMediaTypes.add(Converter.toJaxRsMediaType(mediaType));
            }
            this.acceptedMediaTypes = Collections
                    .unmodifiableList(accMediaTypes);
        }
        return this.acceptedMediaTypes;
    }

    /**
     * Returns the accepted media types as Restlet
     * {@link org.restlet.data.MediaType}s.
     *
     * @return the accepted {@link org.restlet.data.MediaType}s.
     */
    public SortedMetadata<org.restlet.data.MediaType> getAccMediaTypes() {
        return this.accMediaTypes;
    }

    /**
     * current state of the matchedResources
     *
     * @see javax.ws.rs.core.UriInfo#getMatchedResources()
     */
    List<Object> getMatchedResources() {
        return this.matchedResources;
    }

    /**
     * current state of the matchedURIs
     *
     * @see javax.ws.rs.core.UriInfo#getMatchedURIs()
     */
    List<String> getMatchedURIs() {
        return this.matchedURIs;
    }

    /**
     * Returns the string value of the authentication scheme used to protect the
     * resource. If the resource is not authenticated, null is returned.
     *
     * Values are the same as the CGI variable AUTH_TYPE
     *
     * @return one of the static members BASIC_AUTH, FORM_AUTH,
     *         CLIENT_CERT_AUTH, DIGEST_AUTH (suitable for == comparison) or the
     *         container-specific string indicating the authentication scheme,
     *         or null if the request was not authenticated.
     * @see SecurityContext#getAuthenticationScheme()
     */
    public String getAuthenticationScheme() {
        if (SecurityUtil.isSslClientCertAuth(this.request)) {
            return SecurityContext.CLIENT_CERT_AUTH;
        }
        ChallengeResponse challengeResponse = request.getChallengeResponse();
        if (challengeResponse == null) {
            return null;
        }
        if (!request.getClientInfo().isAuthenticated()) {
            return null;
        }
        final ChallengeScheme authScheme = challengeResponse.getScheme();
        if (authScheme == null) {
            return null;
        }
        if (authScheme.equals(ChallengeScheme.HTTP_BASIC)) {
            return SecurityContext.BASIC_AUTH;
        }
        if (authScheme.equals(ChallengeScheme.HTTP_DIGEST)) {
            return SecurityContext.DIGEST_AUTH;
        }
        // if (authScheme.equals(ChallengeScheme.HTTPS_CLIENT_CERT))
        // return SecurityContext.CLIENT_CERT_AUTH;
        // if (authScheme.equals(ChallengeScheme.HTTP_SERVLET_FORM))
        // return SecurityContext.FORM_AUTH;
        return authScheme.getName();
    }

    /**
     * Get the base URI of the application. URIs of resource beans are all
     * relative to this base URI.
     *
     * @return the base URI of the application
     * @see UriInfo#getBaseUri()
     */
    public URI getBaseUri() {
        try {
            return new URI(getBaseUriStr());
        } catch (URISyntaxException e) {
            throw wrapUriSyntaxExc(e, unexpectedLogger, "Could not create URI");
        }
    }

    /**
     * Get the absolute path of the request in the form of a UriBuilder. This
     * includes everything preceding the path (host, port etc) but excludes
     * query parameters and fragment.
     *
     * @return a UriBuilder initialized with the absolute path of the request.
     * @see UriInfo#getAbsolutePathBuilder()
     * @see UriInfo#getBaseUriBuilder()
     */
    public UriBuilder getBaseUriBuilder() {
        return UriBuilder.fromUri(getBaseUriStr());
    }

    ExtendedUriBuilder getBaseUriBuilderExtended() {
        ExtendedUriBuilder uriBuilder = ExtendedUriBuilder
                .fromUri(getBaseUriStr());
        ExtendedUriBuilder originalRef = createExtendedUriBuilder(this.referenceOriginal);
        uriBuilder.extension(originalRef.getExtension());
        return uriBuilder;
    }

    private String getBaseUriStr() {
        if (this.baseUri == null) {
            final Reference baseRef = this.referenceCut.getBaseRef();
            if (baseRef != null) {
                this.baseUri = baseRef.toString(false, false);
            }
        }
        return this.baseUri;
    }

    /**
     * Get the request URI extension. The returned string includes any
     * extensions remove during request pre-processing for the purposes of
     * URI-based content negotiation. E.g. if the request URI was:
     *
     * <pre>
     * http://example.com/resource.xml.en
     * </pre>
     *
     * this method would return "xml.en" even if an applications implementation
     * of {@link ApplicationConfig#getMediaTypeMappings()} returned a map that
     * included "xml" as a key
     *
     * @return the request URI extension
     * @see javax.ws.rs.core.UriInfo#getConnegExtension()
     */
    public String getConnegExtension() {
        return referenceOriginal.getExtensions();
    }

    /**
     * Get any cookies that accompanied the request.
     *
     * @return a map of cookie name (String) to Cookie.
     * @see HttpHeaders#getCookies()
     */
    public Map<String, Cookie> getCookies() {
        if (this.cookies == null) {
            final Map<String, Cookie> cookies = new HashMap<String, Cookie>();
            for (final org.restlet.data.Cookie rc : this.request.getCookies()) {
                final Cookie cookie = Converter.toJaxRsCookie(rc);
                cookies.put(cookie.getName(), cookie);
            }
            this.cookies = Collections.unmodifiableMap(cookies);
        }
        return this.cookies;
    }

    /**
     * @see HttpHeaders#getLanguage()
     */
    public Locale getLanguage() {
        if (this.language == null) {
            final Representation entity = this.request.getEntity();
            if (entity == null) {
                return null;
            }
            final List<Language> languages = entity.getLanguages();
            if (languages.isEmpty()) {
                return null;
            }
            this.language = Converter.toLocale(Util.getFirstElement(languages));
        }
        return this.language;
    }

    /**
     * Returns the last matrix parameter with the given name; leaves it encoded.
     *
     * @param matrixParamAnnot
     * @return the last matrix parameter with the given name; leaves it encoded.
     * @see #matrixParamEncIter(MatrixParam)
     */
    public String getLastMatrixParamEnc(MatrixParam matrixParamAnnot) {
        final String mpName = matrixParamAnnot.value();
        final List<PathSegment> pathSegments = getPathSegments(false);
        for (int i = pathSegments.size() - 1; i >= 0; i--) {
            final PathSegment pathSegment = pathSegments.get(i);
            final List<String> mpValues = pathSegment.getMatrixParameters()
                    .get(mpName);
            if ((mpValues != null) && !mpValues.isEmpty()) {
                final String result = Util.getLastElement(mpValues);
                if (result == null) {
                    return "";
                }
                return result;
            }
        }
        return null;
    }

    /**
     * @param annotation
     * @return the last encoded path param with the given name
     * @see #pathParamEncIter(PathParam)
     */
    public String getLastPathParamEnc(PathParam annotation) {
        final String varName = annotation.value();
        final List<String> values = interalGetPathParamsEncoded().get(varName);
        if ((values == null) || values.isEmpty()) {
            return null;
        }
        return Util.getLastElement(values);
    }

    /**
     * @param pathParam
     * @return .
     */
    public String getLastPathSegmentEnc(PathParam pathParam) {
        pathParam.annotationType();
        // TODO CallContext.getLastPathSegmentEnc(PathParam)
        throw new NotYetImplementedException();
    }

    /**
     * Get the media type of the request entity
     *
     * @return the media type or null if there is no request entity.
     * @throws java.lang.IllegalStateException
     *             if called outside the scope of a request
     * @see HttpHeaders#getMediaType()
     */
    public MediaType getMediaType() {
        if (this.mediaType == null) {
            org.restlet.data.MediaType rmt = request.getEntity().getMediaType();
            CharacterSet rCharSet = request.getEntity().getCharacterSet();
            this.mediaType = Converter.toJaxRsMediaType(rmt, rCharSet);
        }
        return this.mediaType;
    }

    /**
     * @see javax.ws.rs.core.Request#getMethod()
     */
    public String getMethod() {
        return this.request.getMethod().getName();
    }

    /**
     * Get the path of the current request relative to the base URI as a string.
     * All sequences of escaped octets are decoded, equivalent to
     * <code>getPath(true)</code>.
     *
     * @return the relative URI path.
     * @see UriInfo#getPath()
     */
    public String getPath() {
        return getPath(true);
    }

    /**
     * Get the path of the current request relative to the base URI as a string.
     *
     * @param decode
     *            controls whether sequences of escaped octets are decoded
     *            (true) or not (false).
     * @return the relative URI path.
     * @see UriInfo#getPath(boolean)
     */
    public String getPath(boolean decode) {
        final String path = this.referenceOriginal.getRelativeRef().toString(
                true, true);
        if (!decode) {
            return path;
        }
        return Reference.decode(path);
    }

    /**
     * Get the values of any embedded URI template parameters. All sequences of
     * escaped octets are decoded, equivalent to
     * <code>getTemplateParameters(true)</code>.
     *
     * @return an unmodifiable map of parameter names and values
     * @throws java.lang.IllegalStateException
     *             if called outside the scope of a request
     * @see javax.ws.rs.Path
     * @see UriInfo#getPathParameters()
     */
    public MultivaluedMap<String, String> getPathParameters() {
        if (this.pathParametersDecoded == null) {
            final MultivaluedMapImpl<String, String> pathParamsDec = new MultivaluedMapImpl<String, String>();
            for (final Map.Entry<String, List<String>> entryEnc : interalGetPathParamsEncoded()
                    .entrySet()) {
                final String keyDec = Reference.decode(entryEnc.getKey());
                final List<String> valuesEnc = entryEnc.getValue();
                List<String> valuesDec = new ArrayList<String>(valuesEnc.size());
                for (final String valueEnc : valuesEnc) {
                    valuesDec.add(Reference.decode(valueEnc));
                }
                pathParamsDec.put(keyDec, valuesDec);
            }
            UnmodifiableMultivaluedMap<String, String> ppd;
            ppd = UnmodifiableMultivaluedMap.get(pathParamsDec, false);
            if (isChangeable()) {
                return ppd;
            }
            this.pathParametersDecoded = ppd;
        }
        return this.pathParametersDecoded;
    }

    /**
     * Get the values of any embedded URI template parameters.
     *
     * @param decode
     *            controls whether sequences of escaped octets are decoded
     *            (true) or not (false).
     * @return an unmodifiable map of parameter names and values
     * @throws java.lang.IllegalStateException
     *             if called outside the scope of a request
     * @see javax.ws.rs.Path
     * @see UriInfo#getPathParameters(boolean)
     */
    public MultivaluedMap<String, String> getPathParameters(boolean decode) {
        if (decode) {
            return getPathParameters();
        }

        return UnmodifiableMultivaluedMap.get(interalGetPathParamsEncoded());
    }

    /**
     * Get the path of the current request relative to the base URI as a list of
     * {@link PathSegment}. This method is useful when the path needs to be
     * parsed, particularly when matrix parameters may be present in the path.
     * All sequences of escaped octets are decoded, equivalent to
     * <code>getPathSegments(true)</code>.
     *
     * @return an unmodifiable list of {@link PathSegment}. The matrix parameter
     *         map of each path segment is also unmodifiable.
     * @throws java.lang.IllegalStateException
     *             if called outside the scope of a request
     * @see PathSegment
     * @see UriInfo#getPathSegments()
     */
    public List<PathSegment> getPathSegments() {
        return getPathSegments(true);
    }

    /**
     * Get the path of the current request relative to the base URI as a list of
     * {@link PathSegment}. This method is useful when the path needs to be
     * parsed, particularly when matrix parameters may be present in the path.
     *
     * @param decode
     *            controls whether sequences of escaped octets are decoded
     *            (true) or not (false).
     * @return an unmodifiable list of {@link PathSegment}. The matrix parameter
     *         map of each path segment is also unmodifiable.
     * @throws java.lang.IllegalStateException
     *             if called outside the scope of a request
     * @see PathSegment
     * @see UriInfo#getPathSegments(boolean)
     */
    public List<PathSegment> getPathSegments(boolean decode) {
        if (decode) {
            if (this.pathSegmentsDecoded == null) {
                this.pathSegmentsDecoded = createPathSegments(true);
            }
            return this.pathSegmentsDecoded;
        }

        if (this.pathSegmentsEncoded == null) {
            this.pathSegmentsEncoded = createPathSegments(false);
        }
        return this.pathSegmentsEncoded;
    }

    /**
     * Get the URI query parameters of the current request. All sequences of
     * escaped octets are decoded, equivalent to
     * <code>getQueryParameters(true)</code>.
     *
     * @return an unmodifiable map of query parameter names and values
     * @throws java.lang.IllegalStateException
     *             if called outside the scope of a request
     * @see UriInfo#getQueryParameters()
     */
    public MultivaluedMap<String, String> getQueryParameters() {
        if (this.queryParametersDecoded == null) {
            this.queryParametersDecoded = UnmodifiableMultivaluedMap
                    .getFromForm(this.referenceOriginal.getQueryAsForm(), false);
        }
        return this.queryParametersDecoded;
    }

    /**
     * Get the URI query parameters of the current request.
     *
     * @param decode
     *            controls whether sequences of escaped octets in parameter
     *            names and values are decoded (true) or not (false).
     * @return an unmodifiable map of query parameter names and values
     * @throws java.lang.IllegalStateException
     *             if called outside the scope of a request
     * @see UriInfo#getQueryParameters(boolean)
     */
    public MultivaluedMap<String, String> getQueryParameters(boolean decode) {
        if (decode) {
            return getQueryParameters();
        }
        if (this.queryParametersEncoded == null) {
            final Form queryForm = Converter
                    .toFormEncoded(this.referenceOriginal.getQuery());
            this.queryParametersEncoded = UnmodifiableMultivaluedMap
                    .getFromForm(queryForm, false);
        }
        return this.queryParametersEncoded;
    }

    /**
     * Returns the Restlet {@link org.restlet.Request}
     *
     * @return the Restlet {@link org.restlet.Request}
     */
    public Request getRequest() {
        return this.request;
    }

    /**
     * @see javax.ws.rs.core.HttpHeaders#getRequestHeader(java.lang.String)
     */
    public List<String> getRequestHeader(String headerName) {
        String[] values;
        values = Util.getHttpHeaders(this.request).getValuesArray(headerName);
        return Collections.unmodifiableList(Arrays.asList(values));
    }

    /**
     * @see HttpHeaders#getRequestHeaders()
     */
    public MultivaluedMap<String, String> getRequestHeaders() {
        if (this.requestHeaders == null) {
            this.requestHeaders = UnmodifiableMultivaluedMap.getFromForm(
                    Util.getHttpHeaders(this.request), false);
        }
        return this.requestHeaders;
    }

    /**
     * @return the absolute request URI
     * @see UriInfo#getRequestUri()
     */
    public URI getRequestUri() {
        try {
            return new URI(this.referenceOriginal.toString(true, true));
        } catch (URISyntaxException e) {
            throw wrapUriSyntaxExc(e, unexpectedLogger, "Could not create URI");
        }
    }

    /**
     * Get the absolute request URI in the form of a UriBuilder.
     *
     * @return a UriBuilder initialized with the absolute request URI.
     * @see UriInfo#getRequestUriBuilder()
     */
    public UriBuilder getRequestUriBuilder() {
        return UriBuilder.fromUri(getRequestUri());
    }

    ExtendedUriBuilder getRequestUriBuilderExtended() {
        return ExtendedUriBuilder.fromUri(getRequestUri());
    }

    /**
     * Returns the Restlet {@link org.restlet.Response}
     *
     * @return the Restlet {@link org.restlet.Response}
     */
    public org.restlet.Response getResponse() {
        return this.response;
    }

    /**
     * Returns a <code>java.security.Principal</code> object containing the name
     * of the current authenticated user. If the user has not been
     * authenticated, the method returns null.
     *
     * @return a <code>java.security.Principal</code> containing the name of the
     *         user making this request; null if the user has not been
     *         authenticated
     * @see SecurityContext#getUserPrincipal()
     */
    public Principal getUserPrincipal() {
        Principal foundPrincipal = (request.getChallengeResponse() == null) ? null
                : request.getChallengeResponse().getPrincipal();

        if (foundPrincipal != null)
            return foundPrincipal;

        return SecurityUtil.getSslClientCertPrincipal(this.request);
    }

    @Override
    public int hashCode() {
        return this.getBaseUriStr().hashCode()
                ^ this.getPathSegments().hashCode()
                ^ this.getPathParameters().hashCode();
    }

    /**
     * @return the pathParametersEncoded
     */
    protected MultivaluedMap<String, String> interalGetPathParamsEncoded() {
        if (this.pathParametersEncoded == null) {
            this.pathParametersEncoded = new MultivaluedMapImpl<String, String>();
        }
        return this.pathParametersEncoded;
    }

    protected boolean isChangeable() {
        return !this.readOnly;
    }

    /**
     * Returns a boolean indicating whether this request was made using a secure
     * channel, such as HTTPS.
     *
     * @return <code>true</code> if the request was made using a secure channel,
     *         <code>false</code> otherwise
     * @see SecurityContext#isSecure()
     */
    public boolean isSecure() {
        return this.request.isConfidential();
    }

    /**
     * Returns a boolean indicating whether the authenticated user is included
     * in the specified logical "role". If the user has not been authenticated,
     * the method returns <code>false</code>.
     *
     * @param roleName
     *            a <code>String</code> specifying the name of the role
     * @return a <code>boolean</code> indicating whether the user making the
     *         request belongs to a given role; <code>false</code> if the user
     *         has not been authenticated
     * @see SecurityContext#isUserInRole(String)
     */
    public boolean isUserInRole(String roleName) {
        Role role = Application.getCurrent().getRole(roleName);
        return (role != null)
                && this.request.getClientInfo().getRoles().contains(role);
    }

    /**
     * @param matrixParamAnnot
     * @return .
     * @see #getLastMatrixParamEnc(MatrixParam)
     */
    public Iterator<String> matrixParamEncIter(MatrixParam matrixParamAnnot) {
        final String mpName = matrixParamAnnot.value();
        return new MatrixParamEncIter(mpName, getPathSegments(false));
    }

    /**
     * @param pathParamAnnot
     * @return .
     * @see #getLastPathParamEnc(PathParam)
     */
    public Iterator<String> pathParamEncIter(PathParam pathParamAnnot) {
        // LATER perhaps this method could be removed, if it is not needed for
        // @PathParam(..) PathSegment
        final String ppName = pathParamAnnot.value();
        List<String> pathParamValues;
        pathParamValues = interalGetPathParamsEncoded().get(ppName);
        if (pathParamValues == null) {
            return EmptyIterator.get();
        }
        return pathParamValues.iterator();
    }

    /**
     * @param pathParam
     * @return .
     */
    public Iterator<String> pathSegementEncIter(PathParam pathParam) {
        pathParam.annotationType();
        // TODO CallContext.pathSegementEncIter(PathParam)
        throw new NotYetImplementedException();
    }

    /**
     * Select the representation variant that best matches the request. More
     * explicit variants are chosen ahead of less explicit ones. A vary header
     * is computed from the supplied list and automatically added to the
     * response.
     *
     * @param variants
     *            a list of Variant that describe all of the available
     *            representation variants.
     * @return the variant that best matches the request.
     * @see Variant.VariantListBuilder
     * @throws IllegalArgumentException
     *             if variants is null or empty.
     * @see javax.ws.rs.core.Request#selectVariant(List)
     */
    public Variant selectVariant(List<Variant> variants)
            throws IllegalArgumentException {
        if ((variants == null) || variants.isEmpty()) {
            throw new IllegalArgumentException();
        }
        final List<org.restlet.representation.Variant> restletVariants = Converter
                .toRestletVariants(variants);
        final org.restlet.representation.Variant bestRestlVar = this.request
                .getClientInfo().getPreferredVariant(restletVariants, null);
        final Variant bestVariant = Converter.toJaxRsVariant(bestRestlVar);
        final Set<Dimension> dimensions = this.response.getDimensions();
        if (bestRestlVar.getCharacterSet() != null) {
            dimensions.add(Dimension.CHARACTER_SET);
        }
        if (bestRestlVar.getEncodings() != null) {
            dimensions.add(Dimension.ENCODING);
        }
        if (bestRestlVar.getLanguages() != null) {
            dimensions.add(Dimension.LANGUAGE);
        }
        if (bestRestlVar.getMediaType() != null) {
            dimensions.add(Dimension.MEDIA_TYPE);
        }
        // NICE add also to JAX-RS-Response, which is possibly not yet
        // generated.
        return bestVariant;
    }

    /**
     * Sets the Context to be read only. As from now changes are not allowed.
     * This method is intended to be used by {@link CallContext#setReadOnly()}.
     * Ignored by {@link #addForMatched(Object, String)}.
     */
    public void setReadOnly() {
        this.readOnly = true;
    }

    @Override
    public String toString() {
        return this.referenceOriginal.toString(true, false);
    }

    /**
     * This method throws an {@link WebApplicationException} for Exceptions
     * where is no planned handling. Logs the exception (warn {@link Level}).
     *
     * @param exc
     *            the catched URISyntaxException
     * @param unexpectedLogger
     *            the unexpectedLogger to log the messade
     * @param logMessage
     *            the message to log.
     * @return Will never return anything, because the generated
     *         WebApplicationException will be thrown. You an formally throw the
     *         returned exception (e.g. in a catch block). So the compiler is
     *         sure, that the method will be left here.
     * @throws WebApplicationException
     *             contains the given {@link Exception}
     */
    private WebApplicationException wrapUriSyntaxExc(URISyntaxException exc,
            Logger logger, String logMessage) throws WebApplicationException {
        logger.log(Level.WARNING, logMessage, exc);
        exc.printStackTrace();
        throw new WebApplicationException(exc,
                javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR);
    }
}
TOP

Related Classes of org.restlet.ext.jaxrs.internal.core.CallContext$MatrixParamEncIter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.