All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.conqat.lib.commons.uniformpath.RelativeUniformPath Maven / Gradle / Ivy

There is a newer version: 2025.1.0-rc2
Show newest version
/*
 * Copyright (c) CQSE GmbH
 *
 * 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.conqat.lib.commons.uniformpath;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;

import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.string.StringUtils;

import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;

/**
 * A relative uniform path that can be resolved against an absolute uniform
 * path. In contrast to absolute uniform paths, relative paths are not typed.
 */
public final class RelativeUniformPath {

	/**
	 * The constituent segments of this uniform path, e.g. for the path
	 * {@code src/main} this would be the array {@code [src, main]}. Segments may
	 * consist of dots or be empty.
	 */
	private final String[] segments;

	private RelativeUniformPath(String... pathSegments) {
		this(Arrays.asList(pathSegments));
	}

	private RelativeUniformPath(Iterable pathSegments) {
		segments = Iterables.toArray(pathSegments, String.class);
	}

	/**
	 * Creates a relative uniform path from the given segments. No resolution of
	 * relative segments (such as {@code ../src}) is attempted at this stage.
	 * 
	 * @see #of(List)
	 */
	public static RelativeUniformPath of(String... segments) {
		Preconditions.checkNotNull(segments, UniformPath.SEGMENTS_LIST_MAY_NOT_BE_NULL);

		return of(Arrays.asList(segments));
	}

	/**
	 * Creates a relative uniform path from the given segments. No resolution of
	 * relative segments (such as {@code ../src}) is attempted at this stage.
	 */
	public static RelativeUniformPath of(List segments) {
		Preconditions.checkNotNull(segments, UniformPath.SEGMENTS_LIST_MAY_NOT_BE_NULL);

		for (String segment : segments) {
			UniformPath.checkSegmentValidity(segment, segments, Objects::isNull, "empty segment");
			UniformPath.checkSegmentValidity(segment, segments, UniformPath::containsUnescapedSlash,
					"contains unescaped slash");
		}
		if (segments.isEmpty()) {
			return new RelativeUniformPath();
		}
		return new RelativeUniformPath(segments);
	}

	/**
	 * Resolves relative path segments such as (such as {@code src/main/../java}) to
	 * the absolute segments of the path (e.g. {@code [src, java]}).
	 */
	/* package */ static List resolveRelativeSegments(List pathSegments) {
		List canonicalSegments = CollectionUtils.filter(pathSegments,
				segment -> !(StringUtils.isEmpty(segment) || segment.equals(".")));
		ListIterator canonicalSegmentsIterator = canonicalSegments.listIterator();
		while (canonicalSegmentsIterator.hasNext()) {
			if (canonicalSegmentsIterator.next().equals("..")) {
				// Remove both previous element and the ".." item
				canonicalSegmentsIterator.previous();
				canonicalSegmentsIterator.remove();
				if (!canonicalSegmentsIterator.hasPrevious()) {
					throw new IllegalArgumentException(
							"Invalid path (refers outside the root folder): " + String.join("/", pathSegments));
				}
				canonicalSegmentsIterator.previous();
				canonicalSegmentsIterator.remove();
			}
		}
		return canonicalSegments;
	}

	/** Returns a read-only view of the segments of this relative uniform path. */
	public List getSegments() {
		return Collections.unmodifiableList(Arrays.asList(segments));
	}

	/** Returns the name of the last segment of this path. */
	public String getLastSegment() {
		if (segments.length == 0) {
			throw new IllegalArgumentException("Cannot get the last segment of the root path");
		}
		return segments[segments.length - 1];
	}

	/**
	 * Creates a new {@link RelativeUniformPath} by prepending the given suffixes to
	 * the relative uniform path.
	 */
	public RelativeUniformPath addSuffix(String... suffix) {
		String[] newSegments = Arrays.copyOf(segments, segments.length + suffix.length);
		System.arraycopy(suffix, 0, newSegments, segments.length, suffix.length);
		return RelativeUniformPath.of(newSegments);
	}

	/**
	 * Resolves this relative path against the given absolute path, performing path
	 * canonicalization in the process (i.e. ".." will be resolved to parent
	 * segment).
	 */
	public UniformPath resolveAgainstAbsolutePath(UniformPath uniformPath) {
		return uniformPath.resolve(this);
	}

	@Override
	public String toString() {
		return String.join("/", segments);
	}

	@Override
	public int hashCode() {
		return Arrays.hashCode(segments);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof RelativeUniformPath)) {
			return false;
		}
		RelativeUniformPath other = (RelativeUniformPath) obj;
		return Arrays.equals(segments, other.segments);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy