OneNoteGuidCollector.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.tika.parser.microsoft.onenote;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.OneNote;
import org.apache.tika.metadata.Property;
/**
* Collects the GUID metadata of one OneNote section for the classic and FSSHTTPB parsers.
* Values are deduplicated per key and capped at {@link #MAX_GUID_COUNT} in total.
*/
public final class OneNoteGuidCollector {
public static final int MAX_GUID_COUNT = 100_000;
public enum Category {
SECTION(OneNote.SECTION_GUID),
PAGE(OneNote.PAGE_GUIDS),
PAGE_SERIES(OneNote.PAGE_SERIES_GUIDS),
PAGE_NODE(OneNote.PAGE_NODE_GUIDS),
CONFLICT_PAGE(OneNote.CONFLICT_PAGE_GUIDS);
private final Property property;
Category(Property property) {
this.property = property;
}
}
private final Map<Category, LinkedHashSet<String>> values = new EnumMap<>(Category.class);
private final Consumer<String> warningHandler;
private int count;
private boolean limitWarningReported;
public OneNoteGuidCollector(Consumer<String> warningHandler) {
this.warningHandler = warningHandler;
for (Category category : Category.values()) {
values.put(category, new LinkedHashSet<>());
}
}
public void add(Category category, String guid) {
if (guid == null) {
return;
}
Set<String> categoryValues = values.get(category);
if (categoryValues.contains(guid)
|| (category == Category.SECTION && !categoryValues.isEmpty())) {
return;
}
if (count >= MAX_GUID_COUNT) {
reportLimitWarning();
return;
}
categoryValues.add(guid);
count++;
if (count == MAX_GUID_COUNT) {
reportLimitWarning();
}
}
/**
* Records a NotebookManagementEntityGuid under the key for the JCID of the object that
* carries it. Section-node objects and unrecognized JCIDs are skipped.
*/
public void addForObjectType(int jcid, String guid) {
Category category = categoryFor(jcid);
if (category != null) {
add(category, guid);
}
}
static Category categoryFor(int jcid) {
switch (jcid) {
case OneNoteJcid.PAGE_METADATA:
return Category.PAGE;
case OneNoteJcid.PAGE_SERIES_NODE:
return Category.PAGE_SERIES;
case OneNoteJcid.PAGE_NODE:
return Category.PAGE_NODE;
case OneNoteJcid.CONFLICT_PAGE_METADATA:
return Category.CONFLICT_PAGE;
default:
return null;
}
}
private void reportLimitWarning() {
if (!limitWarningReported) {
limitWarningReported = true;
warningHandler.accept("Capping OneNote GUID metadata at " + MAX_GUID_COUNT +
" distinct values");
}
}
public Set<String> values(Category category) {
return Collections.unmodifiableSet(values.get(category));
}
public boolean isFull() {
return count >= MAX_GUID_COUNT;
}
/** Sets each non-empty key on {@code metadata}, with values sorted. */
public void publish(Metadata metadata) {
for (Map.Entry<Category, LinkedHashSet<String>> entry : values.entrySet()) {
if (!entry.getValue().isEmpty()) {
if (entry.getKey() == Category.SECTION) {
metadata.set(entry.getKey().property, entry.getValue().iterator().next());
} else {
String[] sorted = entry.getValue().toArray(new String[0]);
Arrays.sort(sorted);
metadata.set(entry.getKey().property, sorted);
}
}
}
}
}