/*
* The MIT License
*
* Copyright 2013 Tim Boudreau.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.mastfrog.acteur.util;
import static com.mastfrog.acteur.util.CacheControlTypes.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/**
*
* @author Tim Boudreau
*/
public final class CacheControl {
private final List<E> entries = new ArrayList<>();
public static CacheControl PUBLIC_MUST_REVALIDATE
= new CacheControl(Public, must_revalidate);
public static CacheControl PUBLIC_MUST_REVALIDATE_MAX_AGE_1_DAY
= new CacheControl(Public, must_revalidate).add(max_age, Duration.standardDays(1));
public static CacheControl PRIVATE_NO_CACHE_NO_STORE
= new CacheControl(Private, no_cache, no_store);
public CacheControl(CacheControlTypes... types) {
for (CacheControlTypes c : types) {
add(c);
}
}
public static CacheControl $(CacheControlTypes types) {
return new CacheControl(types);
}
private final DateTime creationTime = new DateTime();
public boolean isExpired() {
if (contains(CacheControlTypes.no_cache) || contains(CacheControlTypes.no_store)) {
return true;
}
Long maxAgeSeconds = get(CacheControlTypes.max_age);
if (maxAgeSeconds != null) {
Duration dur = new Duration(new DateTime(), creationTime);
Duration target = Duration.standardSeconds(maxAgeSeconds);
if (dur.isLongerThan(target)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
int result = 7;
for (E e : entries) {
result += 79 * e.hashCode();
}
return result;
}
@Override
public boolean equals(Object o) {
if (o instanceof CacheControl) {
Set<E> a = new HashSet<>(entries);
Set<E> b = new HashSet<>(((CacheControl) o).entries);
return a.equals(b);
}
return false;
}
public CacheControl add(CacheControlTypes... types) {
for (CacheControlTypes type : types) {
_add(type);
}
return this;
}
public boolean contains(CacheControlTypes type) {
for (E e : entries) {
if (e.type == type) {
return true;
}
}
return false;
}
public long get(CacheControlTypes type) {
if (!type.takesValue) {
throw new IllegalArgumentException(type + " does not take a value");
}
for (E e : entries) {
if (e.type == type) {
return e.value;
}
}
return -1;
}
void _add(CacheControlTypes type) {
if (type.takesValue) {
throw new IllegalArgumentException(type + " requires a value");
}
for (Iterator<E> it = entries.iterator(); it.hasNext();) {
E e = it.next();
if (e.type == type) {
it.remove();
}
}
entries.add(new E(type));
}
public CacheControl add(CacheControlTypes type, Duration value) {
if (!type.takesValue) {
throw new IllegalArgumentException(type + " requires a value");
}
for (Iterator<E> it = entries.iterator(); it.hasNext();) {
E e = it.next();
if (e.type == type) {
it.remove();
}
}
entries.add(new E(type, value.toStandardSeconds().getSeconds()));
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Iterator<E> it = entries.iterator(); it.hasNext();) {
sb.append(it.next());
if (it.hasNext()) {
sb.append(", ");
}
}
return sb.toString();
}
public boolean isEmpty() {
return entries.isEmpty();
}
public static CacheControl fromString(String s) {
CacheControl result = new CacheControl();
String[] parts = s.split(",");
for (String part : parts) {
part = part.trim();
CacheControlTypes t = CacheControlTypes.find(part);
if (t != null) {
if (t.takesValue) {
String[] sides = part.split("=", 2);
if (sides.length == 2) {
try {
long val = Long.parseLong(sides[1]);
result.entries.add(new E(t, val));
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
Logger.getLogger(CacheControl.class.getName()).log(Level.INFO, "Bad number in cache control header", nfe);
}
}
} else {
result.add(t);
}
} else {
System.err.println("Unrecognized: " + part);
}
}
return result;
}
private static class E {
private final CacheControlTypes type;
private final long value;
E(CacheControlTypes type) {
this(type, -1);
}
E(CacheControlTypes type, long value) {
this.type = type;
this.value = value;
assert type.takesValue || value == -1;
}
@Override
public String toString() {
if (type.takesValue) {
return type + "=" + value;
} else {
return type.toString();
}
}
@Override
public boolean equals(Object o) {
return o instanceof E && ((E) o).type == type && (!type.takesValue || (((E) o).value == value));
}
@Override
public int hashCode() {
int hash = 5;
hash = 23 * hash + this.type.hashCode();
hash = 23 * hash + (int) (this.value ^ (this.value >>> 32));
return hash;
}
}
}