/* * Copyright 2016 The Simple File Server Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sfs.metadata; import com.google.common.collect.SortedSetMultimap; import io.vertx.core.MultiMap; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.google.common.base.Splitter.on; import static com.google.common.collect.TreeMultimap.create; import static java.lang.String.CASE_INSENSITIVE_ORDER; import static java.util.Collections.emptyList; import static org.sfs.protobuf.XVolume.XDumpFile; import static org.sfs.protobuf.XVolume.XDumpFile.Metadata.Builder; import static org.sfs.protobuf.XVolume.XDumpFile.Metadata.Entry; import static org.sfs.protobuf.XVolume.XDumpFile.Metadata.newBuilder; import static org.sfs.util.SfsHttpHeaders.X_ADD_ACCOUNT_META; import static org.sfs.util.SfsHttpHeaders.X_ADD_CONTAINER_META; import static org.sfs.util.SfsHttpHeaders.X_ADD_OBJECT_META; import static org.sfs.util.SfsHttpHeaders.X_REMOVE_ACCOUNT_META; import static org.sfs.util.SfsHttpHeaders.X_REMOVE_CONTAINER_META; import static org.sfs.util.SfsHttpHeaders.X_REMOVE_OBJECT_META; public class Metadata { private static final SortedSet<String> EMPTY_SET = new TreeSet<>(); private SortedSetMultimap<String, String> meta; private final Pattern addParser; private final Pattern removeParser; private Metadata(Pattern addParser, Pattern removeParser) { this.addParser = addParser; this.removeParser = removeParser; this.meta = create(CASE_INSENSITIVE_ORDER, CASE_INSENSITIVE_ORDER); } public static Metadata account() { return new Metadata(X_ADD_ACCOUNT_META, X_REMOVE_ACCOUNT_META); } public static Metadata container() { return new Metadata(X_ADD_CONTAINER_META, X_REMOVE_CONTAINER_META); } public static Metadata object() { return new Metadata(X_ADD_OBJECT_META, X_REMOVE_OBJECT_META); } public SortedSet<String> get(String key) { SortedSet<String> list = meta.get(key); if (list == null) list = EMPTY_SET; return list; } public boolean put(String key, String value) { return meta.put(key, value); } public boolean remove(String key, String value) { return meta.remove(key, value); } public void removeAll(String key) { meta.removeAll(key); } public Metadata clear() { meta.clear(); return this; } public Set<String> keySet() { return meta.keySet(); } public XDumpFile.Metadata toExportObject() { Builder builder = newBuilder(); if (meta != null) { for (Map.Entry<String, Collection<String>> entry : meta.asMap().entrySet()) { String name = entry.getKey(); Collection<String> values = entry.getValue(); Entry metadataEntry = Entry.newBuilder() .setName(name) .addAllValues(values != null ? values : emptyList()) .build(); builder = builder.addEntries(metadataEntry); } } return builder.build(); } public JsonArray toJsonObject() { JsonArray metadata = new JsonArray(); if (!meta.isEmpty()) { for (String key : keySet()) { SortedSet<String> values = get(key); if (!values.isEmpty()) { JsonArray valueJsonArray = new JsonArray(); for (String value : values) { valueJsonArray.add(value); } JsonObject metaObject = new JsonObject(); metaObject.put("name", key); metaObject.put("values", valueJsonArray); metadata.add(metaObject); } } } return metadata; } public Metadata withExportObject(XDumpFile.Metadata metadataArray) { for (Entry entry : metadataArray.getEntriesList()) { String name = entry.getName(); List<String> metaValues = entry.getValuesList(); for (String metaValue : metaValues) { put(name, metaValue); } } return this; } public Metadata withJsonObject(JsonArray metadataArray) { for (Object o : metadataArray) { JsonObject jsonObject = (JsonObject) o; String name = jsonObject.getString("name"); JsonArray valueArray = jsonObject.getJsonArray("values", new JsonArray()); for (Object p : valueArray) { String metaValue = (String) p; put(name, metaValue); } } return this; } public Metadata withHttpHeaders(MultiMap headers) { Set<String> processed = new HashSet<>(); // adds first for (String headerName : headers.names()) { Matcher matcher = addParser.matcher(headerName); if (matcher.find()) { List<String> values = headers.getAll(headerName); if (values != null && !values.isEmpty()) { String metaName = matcher.group(1); removeAll(metaName); for (String value : values) { for (String split : on(',') .omitEmptyStrings() .trimResults() .split(value)) { processed.add(metaName); put(metaName, split); } } } } } // then deletes for (String headerName : headers.names()) { Matcher matcher0 = addParser.matcher(headerName); if (matcher0.find()) { String metaName = matcher0.group(1); if (processed.add(metaName)) { List<String> values = headers.getAll(headerName); boolean hasData = false; if (values != null) { for (String value : values) { if (value != null) { value = value.trim(); if (!value.isEmpty()) { hasData = true; break; } } } } if (!hasData) { removeAll(metaName); } } } Matcher matcher1 = removeParser.matcher(headerName); if (matcher1.find()) { String metaName = matcher1.group(1); if (processed.add(metaName)) { removeAll(metaName); } } } return this; } }