001/*
002 * This file is part of the JDrupes non-blocking HTTP Codec
003 * Copyright (C) 2017 Michael N. Lipp
004 *
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Lesser General Public License as published
007 * by the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
013 * License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.httpcodec.types;
020
021import java.util.Collection;
022import java.util.HashSet;
023import java.util.Iterator;
024import java.util.LinkedHashMap;
025import java.util.Optional;
026import java.util.Set;
027import java.util.stream.Collectors;
028import java.util.stream.Stream;
029
030import org.jdrupes.httpcodec.util.ListItemizer;
031
032/**
033 * Represents a list of Cache-Control directives.
034 * 
035 * @see "[RFC 7234, 5.2](https://tools.ietf.org/html/rfc7234#section-5.2)"
036 */
037public class CacheControlDirectives implements Iterable<Directive> {
038
039        private LinkedHashMap<String, Directive> directives;
040        
041        /**
042         * Creates a new empty cookie list.
043         */
044        public CacheControlDirectives() {
045                directives = new LinkedHashMap<>();
046        }
047
048        /**
049         * Creates a new list with items copied from the existing collection.
050         * 
051         * @param existing the existing collection
052         */
053        public CacheControlDirectives(Collection<Directive> existing) {
054                directives = new LinkedHashMap<>();
055                for (Directive directive: existing) {
056                        directives.put(directive.name(), directive);
057                }
058        }
059
060        /**
061         * Returns the value for the directive with the given name.
062         * 
063         * @param name the name
064         * @return the value if a directive with the given name exists
065         */
066        public Optional<String> valueForName(String name) {
067                return Optional.ofNullable(directives.get(name.toLowerCase()))
068                                .flatMap(Directive::value);
069        }
070
071        /**
072         * Adds a directive to the list. If a directive with the same name
073         * already exists, it is replaced (except for `no-cache`).
074         * 
075         * The `no-cache` directive is handled specially. If no such directive
076         * exists, it is added. Else, if the new directive has no
077         * arguments, it replaces the existing `no-cache` directive. If both
078         * the existing directive and the new directive specify fields,
079         * the fields are merged.
080         * 
081         * @param directive the directive
082         * @return the directives for easy chaining
083         */
084        public CacheControlDirectives add(Directive directive) {
085                if ("no-cache".equals(directive.name())) {
086                        Directive existing = directives.get(directive.name());
087                        if (existing != null) {
088                                if (!existing.value().isPresent()) {
089                                        return this;
090                                }
091                                if (directive.value().isPresent()) {
092                                        Set<String> values = new HashSet<>();
093                                        new ListItemizer(existing.value().get(), ",")
094                                                .forEachRemaining(values::add);
095                                        new ListItemizer(directive.value().get(), ",")
096                                                .forEachRemaining(values::add);
097                                        directives.put(directive.name(), 
098                                                        new Directive(directive.name(), values.stream()
099                                                                        .collect(Collectors.joining(", "))));
100                                        return this;
101                                }
102                        }
103                }
104                directives.remove(directive.name());
105                directives.put(directive.name(), directive);
106                return this;
107        }
108
109        /**
110         * Removes all directives from the list.
111         * 
112         * @return the directives for easy chaining
113         */
114        public CacheControlDirectives clear() {
115                directives.clear();
116                return this;
117        }
118
119        public boolean isEmpty() {
120                return directives.isEmpty();
121        }
122
123        /* (non-Javadoc)
124         * @see java.util.List#iterator()
125         */
126        @Override
127        public Iterator<Directive> iterator() {
128                return directives.values().iterator();
129        }
130
131        public Stream<Directive> stream() {
132                return directives.values().stream();
133        }
134        
135        /**
136         * Remove the directive with the given name.
137         * 
138         * @param name
139         * @return the directives for easy chaining
140         */
141        public boolean remove(String name) {
142                return false;
143        }
144
145        public int size() {
146                return directives.size();
147        }
148
149}