123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- /* ====================================================================
- 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.poi.openxml4j.opc;
-
- import java.io.Serializable;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.HashSet;
- import java.util.Set;
- import java.util.TreeMap;
- import java.util.function.ToIntFunction;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
-
- import com.zaxxer.sparsebits.SparseBitSet;
- import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
- import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
-
- /**
- * A package part collection.
- */
- public final class PackagePartCollection implements Serializable {
-
- private static final long serialVersionUID = 2515031135957635517L;
-
- /**
- * HashSet use to store this collection part names as string for rule
- * M1.11 optimized checking.
- */
- private final Set<String> registerPartNameStr = new HashSet<>();
-
- private final TreeMap<String, PackagePart> packagePartLookup =
- new TreeMap<>(PackagePartName::compare);
-
-
- /**
- * Check rule [M1.11]: a package implementer shall neither create nor
- * recognize a part with a part name derived from another part name by
- * appending segments to it.
- *
- * @param partName name of part
- * @param part part to put
- * @return the previous value associated with {@code partName}, or
- * {@code null} if there was no mapping for {@code partName}.
- * @throws InvalidOperationException
- * Throws if you try to add a part with a name derived from
- * another part name.
- */
- public PackagePart put(final PackagePartName partName, final PackagePart part) {
- final String ppName = partName.getName();
- final StringBuilder concatSeg = new StringBuilder();
- // split at slash, but keep leading slash
- final String delim = "(?=["+PackagingURIHelper.FORWARD_SLASH_STRING+"])";
- for (String seg : ppName.split(delim)) {
- concatSeg.append(seg);
- if (registerPartNameStr.contains(concatSeg.toString())) {
- throw new InvalidOperationException(
- "You can't add a part with a part name derived from another part ! [M1.11]");
- }
- }
- registerPartNameStr.add(ppName);
- return packagePartLookup.put(ppName, part);
- }
-
- public PackagePart remove(PackagePartName key) {
- if (key == null) {
- return null;
- }
- final String ppName = key.getName();
- PackagePart pp = packagePartLookup.remove(ppName);
- if (pp != null) {
- this.registerPartNameStr.remove(ppName);
- }
- return pp;
- }
-
-
- /**
- * The values themselves should be returned in sorted order. Doing it here
- * avoids paying the high cost of Natural Ordering per insertion.
- * @return unmodifiable collection of parts
- */
- public Collection<PackagePart> sortedValues() {
- return Collections.unmodifiableCollection(packagePartLookup.values());
-
- }
-
- public boolean containsKey(PackagePartName partName) {
- return partName != null && packagePartLookup.containsKey(partName.getName());
- }
-
- public PackagePart get(PackagePartName partName) {
- return partName == null ? null : packagePartLookup.get(partName.getName());
- }
-
- public int size() {
- return packagePartLookup.size();
- }
-
-
-
- /**
- * Get an unused part index based on the namePattern, which doesn't exist yet
- * and has the lowest positive index
- *
- * @param nameTemplate
- * The template for new part names containing a {@code '#'} for the index,
- * e.g. "/ppt/slides/slide#.xml"
- * @return the next available part name index
- * @throws InvalidFormatException if the nameTemplate is null or doesn't contain
- * the index char (#) or results in an invalid part name
- */
- public int getUnusedPartIndex(final String nameTemplate) throws InvalidFormatException {
- if (nameTemplate == null || !nameTemplate.contains("#")) {
- throw new InvalidFormatException("name template must not be null and contain an index char (#)");
- }
-
- final Pattern pattern = Pattern.compile(nameTemplate.replace("#", "([0-9]+)"));
-
- final ToIntFunction<String> indexFromName = name -> {
- Matcher m = pattern.matcher(name);
- return m.matches() ? Integer.parseInt(m.group(1)) : 0;
- };
-
- return packagePartLookup.keySet().stream()
- .mapToInt(indexFromName)
- .collect(SparseBitSet::new, SparseBitSet::set, (s1,s2) -> s1.or(s2)).nextClearBit(1);
- }
- }
|