/* * Licensed to DuraSpace under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional information * regarding copyright ownership. * * DuraSpace 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.fcrepo.kernel.modeshape.identifiers; import static com.google.common.base.Joiner.on; import static com.google.common.base.Splitter.fixedLength; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.getLast; import static com.google.common.collect.Lists.transform; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static java.util.UUID.randomUUID; import static org.slf4j.LoggerFactory.getLogger; import java.util.List; import org.fcrepo.kernel.api.identifiers.InternalIdentifierConverter; import org.slf4j.Logger; /** * Injects and extracts segments of hierarchy before the last segment of a * multi-part identifier to ensure efficient performance of the JCR. * * @author ajs6f * @since Mar 26, 2014 */ public class HierarchyConverter extends InternalIdentifierConverter { public static final String DEFAULT_SEPARATOR = "/"; private String separator = DEFAULT_SEPARATOR; private String prefix = ""; private static final int DEFAULT_LENGTH = 2; private static final int DEFAULT_COUNT = 4; private int length = DEFAULT_LENGTH; private int levels = DEFAULT_COUNT; private static final Logger log = getLogger(HierarchyConverter.class); /* * (non-Javadoc) * @see com.google.common.base.Converter#doBackward(java.lang.Object) */ @Override protected String doBackward(final String flat) { log.debug("Converting incoming identifier: {}", flat); final List<String> hierarchySegments = createHierarchySegments(); final List<String> flatSegments = asList(flat.split(separator)); List<String> firstSegments = emptyList(); List<String> lastSegment = emptyList(); if (flatSegments.size() == 0) { // either empty identifier or separator identifier return on(separator).join(hierarchySegments); } if (flatSegments.size() > 1) { lastSegment = singletonList(getLast(flatSegments)); firstSegments = flatSegments.subList(0, flatSegments.size() - 1); } else { // just one segment lastSegment = singletonList(flatSegments.get(0)); } final Iterable<String> allSegments = concat(firstSegments, hierarchySegments, lastSegment); return on(separator).join(allSegments); } /* * (non-Javadoc) * @see com.google.common.base.Converter#doForward(java.lang.Object) */ @Override protected String doForward(final String hierarchical) { log.debug("Converting outgoing identifier: {}", hierarchical); final List<String> segments = asList(hierarchical.split(separator)); if (segments.size() <= levels) { // must be a root identifier return ""; } List<String> firstSegments = emptyList(); List<String> lastSegment = emptyList(); if (segments.size() > levels + 1) { // we subtract one for the final segment, then levels for the // inserted hierarchy segments we want to remove firstSegments = segments.subList(0, segments.size() - 1 - levels); lastSegment = singletonList(getLast(segments)); } else { // just the trailing non-hierarchical segment lastSegment = singletonList(getLast(segments)); } return on(separator).join(concat(firstSegments, lastSegment)); } private List<String> createHierarchySegments() { // offers a list of segments sliced out of a UUID, each prefixed if (levels == 0) { return emptyList(); } return transform(fixedLength(length).splitToList(createHierarchyCharacterBlock()), x -> prefix + x); } private CharSequence createHierarchyCharacterBlock() { return randomUUID().toString().replace("-", "").substring(0, length * levels); } /** * @param sep the separator to use */ public void setSeparator(final String sep) { this.separator = sep; } /** * @param l the length to set */ public void setLength(final int l) { if (l < 1) { throw new IllegalArgumentException("Segment length must be at least one!"); } this.length = l; } /** * @param l the levels to set */ public void setLevels(final int l) { this.levels = l; } /** * @param p the prefix to set */ public void setPrefix(final String p) { this.prefix = p; } }