001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance 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.fcrepo.http.commons.domain;
019
020import org.glassfish.jersey.message.internal.HttpHeaderReader;
021
022import javax.servlet.http.HttpServletResponse;
023import java.text.ParseException;
024import java.util.HashMap;
025import java.util.Map;
026import java.util.Optional;
027
028/**
029 * Parse a single prefer tag, value and any optional parameters
030 *
031 * @author cabeer
032 */
033public class PreferTag implements Comparable<PreferTag> {
034    private final String tag;
035    private String value = "";
036    private Map<String, String> params = new HashMap<>();
037
038    /**
039     * Create an empty PreferTag
040     * @return the empty PreferTag
041     */
042    public static PreferTag emptyTag() {
043        return new PreferTag((String)null);
044    }
045
046    /**
047     * Create a new PreferTag from an existing tag
048     * @param preferTag the preferTag
049     */
050    public PreferTag(final PreferTag preferTag) {
051        tag = preferTag.getTag();
052        value = preferTag.getValue();
053        params = preferTag.getParams();
054    }
055
056    /**
057     * Parse the prefer tag and parameters out of the header
058     * @param reader the reader
059     */
060    public PreferTag(final HttpHeaderReader reader) {
061
062        // Skip any white space
063        reader.hasNext();
064
065        if (reader.hasNext()) {
066            try {
067                tag = Optional.ofNullable(reader.nextToken())
068                          .map(CharSequence::toString).orElse(null);
069
070                if (reader.hasNextSeparator('=', true)) {
071                    reader.next();
072
073                    value = Optional.ofNullable(reader.nextTokenOrQuotedString())
074                            .    map(CharSequence::toString)
075                                .orElse(null);
076                }
077
078                if (reader.hasNext()) {
079                    params = HttpHeaderReader.readParameters(reader);
080                    if ( params == null ) {
081                        params = new HashMap<>();
082                    }
083                }
084            } catch (ParseException e) {
085                throw new IllegalArgumentException("Could not parse 'Prefer' header", e);
086            }
087        } else {
088            tag = "";
089        }
090    }
091
092    /**
093     * Create a blank prefer tag
094     * @param inputTag the input tag
095     */
096    public PreferTag(final String inputTag) {
097        this(HttpHeaderReader.newInstance(inputTag));
098    }
099
100    /**
101     * Get the tag name
102     * @return tag name
103     */
104    public String getTag() {
105        return tag;
106    }
107
108    /**
109     * Get the default value for the tag
110     * @return default value for the tag
111     */
112    public String getValue() {
113        return value;
114    }
115
116    /**
117     * Get any additional parameters for the prefer tag
118     * @return additional parameters for the prefer tag
119     */
120    public Map<String,String> getParams() {
121        return params;
122    }
123
124    /**
125     * Add appropriate response headers to indicate that the incoming preferences were acknowledged
126     * @param servletResponse the servlet response
127     */
128    public void addResponseHeaders(final HttpServletResponse servletResponse) {
129        if (!value.equals("minimal")) {
130            servletResponse.addHeader("Preference-Applied", "return=representation");
131        } else {
132            servletResponse.addHeader("Preference-Applied", "return=minimal");
133        }
134        servletResponse.addHeader("Vary", "Prefer");
135    }
136
137    /**
138     * We consider tags with the same name to be equal, because <a
139     * href="http://tools.ietf.org/html/rfc7240#page-4">the definition of Prefer headers</a> does not permit that tags
140     * with the same name be consumed except by selecting for the first appearing tag.
141     *
142     * @see java.lang.Comparable#compareTo(java.lang.Object)
143     */
144    @Override
145    public int compareTo(final PreferTag otherTag) {
146        return getTag().compareTo(otherTag.getTag());
147    }
148
149    @Override
150    public boolean equals(final Object obj) {
151        if ((obj != null) && (obj instanceof PreferTag)) {
152            return getTag().equals(((PreferTag) obj).getTag());
153        }
154        return false;
155    }
156
157    @Override
158    public int hashCode() {
159        if (getTag() == null) {
160            return 0;
161        }
162        return getTag().hashCode();
163    }
164}