001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *         http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.w3.ldp.testsuite.matcher;
019
020import java.util.regex.Pattern;
021
022import org.apache.commons.lang3.StringUtils;
023import org.hamcrest.BaseMatcher;
024import org.hamcrest.CustomTypeSafeMatcher;
025import org.hamcrest.Description;
026import org.hamcrest.Matcher;
027import org.jboss.resteasy.plugins.delegates.LinkDelegate;
028import org.w3.ldp.testsuite.http.MediaTypes;
029
030import javax.ws.rs.core.Link;
031
032/**
033 * Matcher collection to work with HttpHeaders.
034 */
035public class HeaderMatchers {
036
037        private static final Pattern TURTLE_REGEX = Pattern.compile("^" + MediaTypes.TEXT_TURTLE + "\\s*(;|$)");
038        /**
039         * Regular expression matching valid ETag values.
040         *
041         * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19">HTTP 1.1: Section 14.19 - ETag</a>
042         */
043        public final static String ETAG_REGEX = "^(W/)?\"([^\"]|\\\\\")*\"$";
044
045        public static Matcher<String> headerPresent() {
046                return new BaseMatcher<String>() {
047                        @Override
048                        public boolean matches(Object item) {
049                                return item != null && StringUtils.isNotBlank(item.toString());
050                        }
051
052                        @Override
053                        public void describeTo(Description description) {
054                                description.appendText("set");
055                        }
056                };
057        }
058
059        public static Matcher<String> headerNotPresent() {
060                return new BaseMatcher<String>() {
061                        @Override
062                        public boolean matches(Object item) {
063                                return item == null || StringUtils.isBlank(item.toString());
064                        }
065
066                        @Override
067                        public void describeTo(Description description) {
068                                description.appendText("absent or empty");
069                        }
070                };
071        }
072
073        public static Matcher<String> isLink(String uri, String rel) {
074                final Link expected = Link.fromUri(uri).rel(rel).build();
075                return new CustomTypeSafeMatcher<String>(String.format("a Link-Header to <%s> with rel='%s'", uri, rel)) {
076                        @Override
077                        protected boolean matchesSafely(String item) {
078                                return expected.equals(new LinkDelegate().fromString(item));
079                        }
080                };
081        }
082
083
084        public static Matcher<String> isValidEntityTag() {
085                return new CustomTypeSafeMatcher<String>("a valid EntityTag value as defined in RFC2616 section 14.19 (did you quote the value?)") {
086                        /**
087                         * Checks that the ETag value is present and valid as defined in RFC2616
088                         *
089                         * @param item
090                         *                        the header value
091                         * @return true only if the ETag is valid
092                         *
093                         * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19">HTTP 1.1: Section 14.19 - ETag</a>
094                         */
095                        @Override
096                        protected boolean matchesSafely(String item) {
097                                return item.trim().matches(ETAG_REGEX);
098                        }
099                };
100        }
101
102        /**
103         * Matcher testing a Content-Type response header's compatibility with
104         * JSON-LD (expects application/ld+json or application/json).
105         *
106         * @return the matcher
107         */
108        public static Matcher<String> isJsonLdCompatibleContentType() {
109                return new CustomTypeSafeMatcher<String>("application/ld+json or application/json") {
110                        @Override
111                        protected boolean matchesSafely(String item) {
112                                return item.equals(MediaTypes.APPLICATION_LD_JSON) || item.equals(MediaTypes.APPLICATION_JSON);
113                        }
114                };
115        }
116
117        /**
118         * Matcher testing a Content-Type response header's compatibility with
119         * Turtle (expects text/turtle).
120         *
121         * @return the matcher
122         */
123        public static Matcher<String> isTurtleCompatibleContentType() {
124                return new CustomTypeSafeMatcher<String>("text/turtle") {
125                        @Override
126                        protected boolean matchesSafely(String item) {
127                                return MediaTypes.TEXT_TURTLE.equals(item)
128                                                || TURTLE_REGEX.matcher(item).find();
129                        }
130                };
131        }
132}