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

org.eclipse.jetty.util.resource.CombinedResource Maven / Gradle / Ivy

There is a newer version: 12.1.0.alpha0
Show newest version
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.util.resource;

import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.stream.Collectors;

import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.URIUtil;

/**
 * Multiple {@link Resource} directories presented as a single overlayed {@link Resource} directory.
 * 

This class differs from a {@link List}<{@link Resource}>, as a relative path can {@link #resolve(String) resolve} * to only a single {@link Resource} in a {@code CombinedResource}, whilst it may resolve to multiple in a * {@link List}<{@link Resource}>.

*/ public class CombinedResource extends Resource { /** *

Make a Resource containing a combination of multiple directory resources.

* @param resources multiple directory resources to combine as a single resource. Order is significant. * @return A Resource of multiple resources or a single resource if only 1 is passed, or null if none are passed. * Any Resource returned will always return {@code true} from {@link Resource#isDirectory()} * @throws IllegalArgumentException if a non-directory resource is passed. * @see CombinedResource */ static Resource combine(List resources) { resources = CombinedResource.gatherUniqueFlatResourceList(resources); if (resources == null || resources.isEmpty()) return null; if (resources.size() == 1) return resources.get(0); return new CombinedResource(resources); } static List gatherUniqueFlatResourceList(List resources) { if (resources == null || resources.isEmpty()) return null; List unique = new ArrayList<>(resources.size()); for (Resource r : resources) { if (r == null) { throw new IllegalArgumentException("Null Resource entry encountered"); } if (r instanceof CombinedResource resourceCollection) { unique.addAll(gatherUniqueFlatResourceList(resourceCollection.getResources())); } else { if (unique.contains(r)) { // skip, already seen continue; } if (!r.exists()) { throw new IllegalArgumentException("Does not exist: " + r); } if (!r.isDirectory()) { throw new IllegalArgumentException("Non-Directory not allowed: " + r); } unique.add(r); } } return unique; } private final List _resources; /** * Instantiates a new resource collection. * * @param resources the resources to be added to collection */ CombinedResource(List resources) { _resources = Collections.unmodifiableList(resources); } /** * Retrieves the resource collection's resources. * * @return the resource collection */ public List getResources() { return _resources; } /** * Resolves a path against the resource collection. * * @param subUriPath The path segment to resolve * @return The resulting resource : *
    *
  • is a file that exists in at least one of the collection, then the first one found is returned
  • *
  • is a directory that exists in at exactly one of the collection, then that simple directory resource is returned
  • *
  • is a directory that exists in several of the collection, then a ResourceCollection of those directories is returned
  • *
  • is null if not found in any entry in this collection
  • *
*/ @Override public Resource resolve(String subUriPath) { if (URIUtil.isNotNormalWithinSelf(subUriPath)) throw new IllegalArgumentException(subUriPath); if (subUriPath.length() == 0 || "/".equals(subUriPath)) { return this; } ArrayList resources = null; // Attempt a simple (single) Resource lookup that exists Resource resolved = null; Resource notFound = null; for (Resource res : _resources) { resolved = res.resolve(subUriPath); if (!Resources.missing(resolved) && !resolved.isDirectory()) return resolved; // Return simple (non-directory) Resource if (Resources.missing(resolved) && notFound == null) notFound = resolved; if (resources == null) resources = new ArrayList<>(); resources.add(resolved); } if (resources == null) return notFound; // This will not exist if (resources.size() == 1) return resources.get(0); return new CombinedResource(resources); } @Override public boolean exists() { for (Resource r : _resources) if (r.exists()) return true; return false; } @Override public Path getPath() { int exists = 0; Path path = null; for (Resource r : _resources) { if (r.exists() && exists++ == 0) path = r.getPath(); } return switch (exists) { case 0 -> _resources.get(0).getPath(); case 1 -> path; default -> null; }; } @Override public String getName() { return null; } @Override public String getFileName() { String filename = null; // return a non-null filename only if all resources agree on the same name. for (Resource r : _resources) { String fn = r.getFileName(); if (fn == null) return null; if (filename == null) filename = fn; else if (!filename.equals(fn)) return null; } return filename; } @Override public URI getURI() { int exists = 0; URI uri = null; for (Resource r : _resources) { if (r.exists() && exists++ == 0) uri = r.getURI(); } return switch (exists) { case 0 -> _resources.get(0).getURI(); case 1 -> uri; default -> null; }; } @Override public boolean isDirectory() { return true; } @Override public boolean isReadable() { for (Resource r : _resources) { if (r.isReadable()) return true; } return false; } @Override public Instant lastModified() { Instant instant = null; for (Resource r : _resources) { Instant lm = r.lastModified(); if (instant == null || lm.isAfter(instant)) { instant = lm; } } return instant; } @Override public long length() { return -1; } @Override public Iterator iterator() { return _resources.iterator(); } @Override public List list() { Map results = new TreeMap<>(); for (Resource base : _resources) { for (Resource r : base.list()) { if (r.isDirectory()) results.computeIfAbsent(r.getFileName(), this::resolve); else results.putIfAbsent(r.getFileName(), r); } } return new ArrayList<>(results.values()); } @Override public void copyTo(Path destination) throws IOException { Collection all = getAllResources(); for (Resource r : all) { if (!r.exists()) continue; Path relative = getPathTo(r); Path pathTo = IO.resolvePath(destination, relative); if (r.isDirectory()) { IO.ensureDirExists(pathTo); } else { IO.ensureDirExists(pathTo.getParent()); r.copyTo(pathTo); } } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CombinedResource other = (CombinedResource)o; return Objects.equals(_resources, other._resources); } @Override public int hashCode() { return Objects.hash(_resources); } @Override public boolean isAlias() { for (Resource r : _resources) { if (r.isAlias()) return true; } return false; } @Override public URI getRealURI() { if (!isAlias()) return getURI(); int exists = 0; URI uri = null; for (Resource r : _resources) { if (r.exists() && exists++ == 0) uri = r.getRealURI(); } return switch (exists) { case 0 -> _resources.get(0).getRealURI(); case 1 -> uri; default -> null; }; } /** * @return the list of resources */ @Override public String toString() { return _resources.stream() .map(Resource::toString) .collect(Collectors.joining(", ", "[", "]")); } @Override public boolean contains(Resource other) { // Every resource from the (possibly combined) other resource ... loop: for (Resource o : other) { // Must be contained in at least one of this resources for (Resource r : _resources) { if (r.contains(o)) continue loop; } // A resource of the other did not match any in this return false; } return true; } @Override public Path getPathTo(Resource other) { Path otherPath = other.getPath(); // If the other resource has a single Path if (otherPath != null) { // return true it's relative location to the first matching resource. for (Resource r : _resources) { if (!r.exists()) continue; Path path = r.getPath(); if (otherPath.startsWith(path)) return path.relativize(otherPath); } return null; } // otherwise the other resource must also be some kind of combined resource. // So every resource in the other combined must have the same relative relationship to us Path relative = null; loop : for (Resource o : other) { if (!o.exists()) continue; for (Resource r : _resources) { if (!r.exists()) continue; if (o.getPath().startsWith(r.getPath())) { Path rel = r.getPath().relativize(o.getPath()); if (relative == null) relative = rel; else if (!relative.equals(rel)) return null; continue loop; } } return null; } return relative; } @Override public boolean isSameFile(Path path) { for (Resource r : this) { if (r.isSameFile(path)) return true; } return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy