xy.reflect.ui.info.ResourcePath Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (C) 2018 OTK Software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* The GNU General Public License allows you also to freely redistribute
* the libraries under the same license, if you provide the terms of the
* GNU General Public License with them and add the following
* copyright notice at the appropriate place (with a link to
* http://javacollection.net/reflectionui/ web site when possible).
******************************************************************************/
package xy.reflect.ui.info;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.xml.bind.annotation.XmlTransient;
import xy.reflect.ui.util.FileUtils;
/**
* This is a renderer-independent resource location class. It allows to specify
* how to access a resource from the heap, the class-path or the file system.
*
* It will also detect alternative resource location strategies is some exist
* for the current resource.
*
* Note that objects of this class are just resource location specifications.
* Engines must be created to provide the actual resource access routines.
*
* @author olitank
*
*/
public class ResourcePath implements Serializable {
private static final long serialVersionUID = 1L;
protected static final String CLASSPATH_RESOURCE_PREFIX = " ";
protected static final String MEMORY_OBJECT_PREFIX = " ";
protected static final int SELF_ALTERNATIVE_INDEX = 0;
protected String path;
protected PathKind pathKind;
protected int chosenAlternativeIndex;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
setSpecification(getSpecification());
}
private void writeObject(ObjectOutputStream out) throws IOException {
setSpecification(getSpecification());
out.defaultWriteObject();
}
/**
* Resource location strategy enumeration.
*
* @author olitank
*
*/
public enum PathKind {
CLASS_PATH_RESOURCE, ABSOLUTE_FILE, RELATIVE_FILE, MEMORY_OBJECT
}
/**
* Constructs an empty resource path. {@link #setSpecification(String)} can then
* be used to specify the resource location.
*/
public ResourcePath() {
this("");
}
/**
* Constructs a class-path resource path.
*
* @param classInResourcePackage
* A class in the same package of the resource.
* @param resourceName
* The name of the resource.
*/
public ResourcePath(Class classInResourcePackage, String resourceName) {
this(ResourcePath.specifyClassPathResourceSpecification(
classInResourcePackage.getPackage().getName().replace(".", "/") + "/" + resourceName));
}
/**
* Constructs a resource path from a specification. Specifications can be
* created using the static specify*Location(String) methods of this class.
*
* @param specification
* The specification.
*/
public ResourcePath(String specification) {
setSpecification(specification);
}
/**
* @param path
* A path that could be used as the argument of
* {@link Class#getResource(String)}
* @return a class-path resource location specification string that can be
* passed to the {@link #setSpecification(String)} method.
*/
public static String specifyClassPathResourceSpecification(String path) {
return ResourcePath.CLASSPATH_RESOURCE_PREFIX + path;
}
/**
* @param path
* A arbitrary path that will uniquely identify a heap object.
* @return a heap resource location specification string that can be passed to
* the {@link #setSpecification(String)} method.
*/
public static String specifyMemoryObjectSpecification(String path) {
return ResourcePath.MEMORY_OBJECT_PREFIX + path;
}
/**
* @param specification
* The full resource location specification string.
* @return the path that was passed to the
* {@link #specifyClassPathResourceSpecification(String)} method in
* order to create the given resource location specification.
*/
public static String extractClassPathResourceLocation(String specification) {
return specification.substring(ResourcePath.CLASSPATH_RESOURCE_PREFIX.length());
}
/**
* @param specification
* The full resource location specification string.
* @return the path that was passed to the
* {@link #specifyMemoryObjectSpecification(String)} method in order to
* create the given resource location specification.
*/
public static String extractMemoryObjectLocation(String specification) {
return specification.substring(ResourcePath.MEMORY_OBJECT_PREFIX.length());
}
/**
* @return the raw path to resource. The format depends on the path kind.
*/
public String getPath() {
return path;
}
/**
* This method differs from {@link #getDefaultSpecification()} by returning an
* alternative specification if one was selected using the
* {@link #setChosenAlternative(ResourcePath)} method.
*
* @return the chosen or default full resource location specification string.
*/
public String getSpecification() {
return getChosen().getDefaultSpecification();
}
/**
* @return the full resource location specification string provided through the
* {@link #setSpecification(String)} method.
*/
public String getDefaultSpecification() {
if (pathKind == PathKind.CLASS_PATH_RESOURCE) {
return ResourcePath.specifyClassPathResourceSpecification(path);
} else if (pathKind == PathKind.MEMORY_OBJECT) {
return ResourcePath.specifyMemoryObjectSpecification(path);
} else {
return path;
}
}
/**
* Sets the full resource location specification string.
*
* @param specification
* The full resource location specification string.
*/
public void setSpecification(String specification) {
if (specification.startsWith(ResourcePath.CLASSPATH_RESOURCE_PREFIX)) {
path = extractClassPathResourceLocation(specification);
pathKind = PathKind.CLASS_PATH_RESOURCE;
} else if (specification.startsWith(ResourcePath.MEMORY_OBJECT_PREFIX)) {
path = extractMemoryObjectLocation(specification);
pathKind = PathKind.MEMORY_OBJECT;
} else {
File file = new File(specification);
path = file.getPath();
pathKind = file.isAbsolute() ? PathKind.ABSOLUTE_FILE : PathKind.RELATIVE_FILE;
}
chosenAlternativeIndex = 0;
}
/**
* @return the current resource location strategy.
*/
public PathKind getPathKind() {
ResourcePath chosen = getChosen();
return chosen.pathKind;
}
/**
* Allows to specify the location of a file system resource.
*
* @param file
* The file system resource path.
*/
public void setFile(File file) {
path = file.getPath();
pathKind = file.isAbsolute() ? PathKind.ABSOLUTE_FILE : PathKind.RELATIVE_FILE;
chosenAlternativeIndex = 0;
}
/**
* @return the current or the chosen resource location alternative if one was
* selected using the {@link #setChosenAlternative(ResourcePath)}
* method.
*/
protected ResourcePath getChosen() {
if (chosenAlternativeIndex == SELF_ALTERNATIVE_INDEX) {
return this;
} else {
return getChosenAlternative();
}
}
/**
* @return the the chosen resource location alternative if one was selected
* using the {@link #setChosenAlternative(ResourcePath)} method, or null
* if no choice was made.
*/
@XmlTransient
public ResourcePath getChosenAlternative() {
List options = getAlternativeOptions();
if ((chosenAlternativeIndex >= 0) && (chosenAlternativeIndex < options.size())) {
return options.get(chosenAlternativeIndex);
}
return null;
}
/**
* Allows to select a resource location alternative.
*
* @param chosenAlternative
* An alternative resource location. Must be included in the list
* returned by the {@link #getAlternativeOptions()} method.
*/
public void setChosenAlternative(ResourcePath chosenAlternative) {
List options = getAlternativeOptions();
chosenAlternativeIndex = options.indexOf(chosenAlternative);
}
/**
* @return the list of detected alternative resource locations, each one with a
* different strategy.
*/
public List getAlternativeOptions() {
List result = new ArrayList();
if ((pathKind == PathKind.ABSOLUTE_FILE) || (pathKind == PathKind.RELATIVE_FILE)) {
if (path.trim().length() > 0) {
File file = new File(path);
result.addAll(findMatchingClassPathResources(file));
if (pathKind == PathKind.ABSOLUTE_FILE) {
File currentDir = new File(".");
if (FileUtils.isAncestor(currentDir, file)) {
File relativeFile = FileUtils.relativizeFile(currentDir, file);
result.add(new ResourcePath(relativeFile.getPath()));
}
}
if (pathKind == PathKind.RELATIVE_FILE) {
result.add(new ResourcePath(file.getAbsolutePath()));
}
}
}
result.add(SELF_ALTERNATIVE_INDEX, this);
return result;
}
protected Collection findMatchingClassPathResources(File file) {
List result = new ArrayList();
File candidateResourceFile = new File(file.getAbsoluteFile().getPath());
while (true) {
File mostAncestorFile = candidateResourceFile.getParentFile();
if (mostAncestorFile == null) {
break;
}
while (mostAncestorFile.getParentFile() != null) {
mostAncestorFile = mostAncestorFile.getParentFile();
}
candidateResourceFile = FileUtils.relativizeFile(mostAncestorFile, candidateResourceFile);
String candidateResourcePath = candidateResourceFile.getPath().replaceAll("\\\\", "/");
URL resourceURL = ResourcePath.class.getClassLoader().getResource(candidateResourcePath);
if (resourceURL != null) {
result.add(new ResourcePath(specifyClassPathResourceSpecification(candidateResourcePath)));
}
}
return result;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + chosenAlternativeIndex;
result = prime * result + ((path == null) ? 0 : path.hashCode());
result = prime * result + ((pathKind == null) ? 0 : pathKind.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ResourcePath other = (ResourcePath) obj;
if (chosenAlternativeIndex != other.chosenAlternativeIndex)
return false;
if (path == null) {
if (other.path != null)
return false;
} else if (!path.equals(other.path))
return false;
if (pathKind != other.pathKind)
return false;
return true;
}
@Override
public String toString() {
return "ResourcePath [path=" + path + ", pathKind=" + pathKind + ", chosenAlternativeIndex="
+ chosenAlternativeIndex + ", getSpecification()=" + getSpecification() + "]";
}
}