Help - Added a task to find unused help images; fixed showing in-memory

images in help
This commit is contained in:
dragonmacher 2019-04-24 17:48:44 -04:00
parent e8dcb3d0c4
commit 49436c44a9
12 changed files with 275 additions and 131 deletions

View file

@ -25,6 +25,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import help.validator.location.*;
import resources.IconProvider;
import resources.Icons;
public class HelpBuildUtils {
@ -536,6 +537,7 @@ public class HelpBuildUtils {
* locate files based upon relative references, specialized help system references (i.e.,
* help/topics/...), and absolute URLs.
*
* @param sourceFile the source file path of the image reference
* @param ref the reference text
* @return an absolute path; null if the URI is remote
* @throws URISyntaxException
@ -544,15 +546,21 @@ public class HelpBuildUtils {
throws URISyntaxException {
if (Icons.isIconsReference(ref)) {
// help system syntax: <img src="Icons.ERROR_ICON" />
URL url = Icons.getUrlForIconsReference(ref);
if (url == null) {
IconProvider iconProvider = Icons.getIconForIconsReference(ref);
if (iconProvider == null || iconProvider.isInvalid()) {
// bad icon name
return ImageLocation.createInvalidRuntimeLocation(sourceFile, ref);
}
URI resolved = url.toURI();
Path path = toPath(resolved);
URL url = iconProvider.getUrl();
URI resolved = null;
Path path = null;
if (url != null) { // we may have an icon with an invalid URL (e.g., a MultiIcon)
resolved = url.toURI();
path = toPath(resolved);
}
return ImageLocation.createRuntimeLocation(sourceFile, ref, resolved, path);
}

View file

@ -21,6 +21,9 @@ import java.nio.file.Path;
/**
* A class that represents the original location of an IMG tag along with its location
* resolution within the help system.
*
* <p>Some images are represented by 'in memory' or 'runtime' values that do not have a valid
* url.
*/
public class ImageLocation {
@ -30,8 +33,13 @@ public class ImageLocation {
private Path resolvedPath;
private URI resolvedUri;
private boolean isRemote;
/** An image that is taken from an image loaded by a Java class (e.g., Icons.XYZ_ICON) */
private boolean isRuntime;
/** A 'runtime' image that could not be located */
private boolean invalidRuntimeImage;
public static ImageLocation createLocalLocation(Path sourceFile, String imageSrc,
URI resolvedUri, Path resolvedPath) {
@ -61,6 +69,7 @@ public class ImageLocation {
l.resolvedPath = null;
l.isRemote = false;
l.isRuntime = true;
l.invalidRuntimeImage = true;
return l;
}
@ -84,48 +93,28 @@ public class ImageLocation {
return sourceFile;
}
public void setSourceFile(Path sourceFile) {
this.sourceFile = sourceFile;
}
public String getImageSrc() {
return imageSrc;
}
public void setImageSrc(String imageSrc) {
this.imageSrc = imageSrc;
}
public Path getResolvedPath() {
return resolvedPath;
}
public void setResolvedPath(Path resolvedPath) {
this.resolvedPath = resolvedPath;
}
public URI getResolvedUri() {
return resolvedUri;
}
public void setResolvedUri(URI resolvedUri) {
this.resolvedUri = resolvedUri;
}
public boolean isRemote() {
return isRemote;
}
public void setRemote(boolean isRemote) {
this.isRemote = isRemote;
}
public boolean isRuntime() {
return isRuntime;
}
public void setRuntime(boolean isRuntime) {
this.isRuntime = isRuntime;
public boolean isInvalidRuntimeImage() {
return invalidRuntimeImage;
}
@Override

View file

@ -120,14 +120,22 @@ public class JavaHelpValidator {
return; // don't even try to verify a remote URL
}
Path imagePath = img.getImageFile();
if (imagePath == null) {
unresolvedLinks.add(new NonExistentIMGFileInvalidLink(img));
if (img.isRuntime()) {
//
// The tool will load this image at runtime--don't perform normal validation
// (runtime means an icon to be loaded from a Java file)
//
if (img.isInvalid()) {
unresolvedLinks.add(new InvalidRuntimeIMGFileInvalidLink(img));
return;
}
return;
}
if (img.isRuntime()) {
// the tool will load this image at runtime--don't perform normal validate
Path imagePath = img.getImageFile();
if (imagePath == null) {
unresolvedLinks.add(new NonExistentIMGFileInvalidLink(img));
return;
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,22 +15,25 @@
*/
package help.validator;
import help.GHelpBuilder;
import help.HelpBuildUtils;
import help.validator.location.HelpModuleLocation;
import help.validator.model.IMG;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import help.HelpBuildUtils;
import help.validator.location.HelpModuleLocation;
import help.validator.model.IMG;
import util.CollectionUtils;
public class UnusedHelpImageFileFinder {
private static final String HELP_PATHS_OPTION = "-hp"; // taken from GHelpBuilder
private static final String DEBUG_SWITCH = "-debug";
private static List<String> moduleHelpPaths;
private static List<String> moduleHelpPaths = new ArrayList<>();
private static boolean debugEnabled = false;
private SortedSet<Path> unusedFiles;
@ -78,24 +80,20 @@ public class UnusedHelpImageFileFinder {
}
public SortedSet<Path> getUnusedImages() {
return new TreeSet<Path>(unusedFiles);
return new TreeSet<>(unusedFiles);
}
private static SortedSet<Path> getUnusedFiles(Collection<IMG> referencedIMGs,
Collection<Path> imageFiles) {
Map<Path, IMG> fileToIMGMap = new HashMap<Path, IMG>();
Map<Path, IMG> fileToIMGMap = new HashMap<>();
for (IMG img : referencedIMGs) {
fileToIMGMap.put(img.getImageFile(), img);
}
SortedSet<Path> set = new TreeSet<Path>(new Comparator<Path>() {
@Override
public int compare(Path f1, Path f2) {
return f1.toUri().toString().toLowerCase().compareTo(
f2.toUri().toString().toLowerCase());
}
});
SortedSet<Path> set =
new TreeSet<>((f1, f2) -> f1.toUri().toString().toLowerCase().compareTo(
f2.toUri().toString().toLowerCase()));
for (Path file : imageFiles) {
IMG img = fileToIMGMap.get(file);
if (img == null && !isExcludedImageFile(file)) {
@ -111,8 +109,9 @@ public class UnusedHelpImageFileFinder {
return absolutePath.indexOf("help/shared/") != -1;
}
private static Collection<IMG> getReferencedIMGs(Collection<HelpModuleLocation> helpCollections) {
Set<IMG> set = new HashSet<IMG>();
private static Collection<IMG> getReferencedIMGs(
Collection<HelpModuleLocation> helpCollections) {
Set<IMG> set = new HashSet<>();
for (HelpModuleLocation help : helpCollections) {
Collection<IMG> IMGs = help.getAllIMGs();
set.addAll(IMGs);
@ -122,7 +121,7 @@ public class UnusedHelpImageFileFinder {
private static Collection<Path> getAllImagesOnDisk(
Collection<HelpModuleLocation> helpDirectories) {
List<Path> files = new ArrayList<Path>();
List<Path> files = new ArrayList<>();
for (HelpModuleLocation help : helpDirectories) {
Path helpDir = help.getHelpLocation();
gatherImageFiles(helpDir, files);
@ -155,10 +154,10 @@ public class UnusedHelpImageFileFinder {
private static List<HelpModuleLocation> collectHelp() {
debug("Parsing help dirs...");
List<HelpModuleLocation> helpCollections =
new ArrayList<HelpModuleLocation>(moduleHelpPaths.size());
List<HelpModuleLocation> helpCollections = new ArrayList<>(moduleHelpPaths.size());
for (String helpDirName : moduleHelpPaths) {
// 1) Make sure the help directory exists
// Make sure the help directory exists
File helpDirectoryFile = null;
try {
helpDirectoryFile = new File(helpDirName).getCanonicalFile();
@ -172,14 +171,8 @@ public class UnusedHelpImageFileFinder {
errorMessage("Help directory not found - skipping: " + helpDirName);
continue;
}
File moduleDir = helpDirectoryFile.getParentFile();
File manifestFile = new File(moduleDir, "Module.manifest");
if (!manifestFile.exists()) {
errorMessage("Help directory not inside valid module: " + helpDirName);
continue;
}
// 3) Create the help directory
// Create the help directory
helpCollections.add(HelpBuildUtils.toLocation(helpDirectoryFile));
}
@ -188,8 +181,8 @@ public class UnusedHelpImageFileFinder {
private static void debug(String string) {
if (debugEnabled) {
System.out.println("[" + UnusedHelpImageFileFinder.class.getSimpleName() + "] " +
string);
System.out.println(
"[" + UnusedHelpImageFileFinder.class.getSimpleName() + "] " + string);
}
}
@ -197,7 +190,7 @@ public class UnusedHelpImageFileFinder {
StringBuilder buffy = new StringBuilder();
errorMessage("Usage:\n");
buffy.append("<module help path1[;module help path2;module help path3;...]> [-debug]");
buffy.append("-hp path1[-hp path2 -hp path3 ...]> [-debug]");
errorMessage(buffy.toString());
}
@ -209,23 +202,53 @@ public class UnusedHelpImageFileFinder {
System.exit(1);
}
List<String> argList = Arrays.asList(args);
// get module directory paths
String modulePathsString = argList.get(args.length - 1);
moduleHelpPaths = new ArrayList<String>();
StringTokenizer tokenizer = new StringTokenizer(modulePathsString, File.pathSeparator);
while (tokenizer.hasMoreTokens()) {
moduleHelpPaths.add(tokenizer.nextToken());
List<String> argList = CollectionUtils.asList(args);
int debugIndex = argList.indexOf(DEBUG_SWITCH);
if (debugIndex > -1) {
debugEnabled = true;
argList.remove(debugIndex);
}
Map<Integer, String> mapped = new TreeMap<>();
for (int i = 0; i < argList.size(); i++) {
mapped.put(i, argList.get(i));
}
for (int i = 0; i < argList.size(); i++) {
String opt = argList.get(i);
if (opt.equals(HELP_PATHS_OPTION)) {
if (i >= argList.size()) {
errorMessage(HELP_PATHS_OPTION + " requires an argument");
printUsage();
System.exit(1);
}
mapped.remove(i);
String paths = mapped.remove(++i);
if (StringUtils.isBlank(paths)) {
errorMessage(HELP_PATHS_OPTION + " requires an argument");
printUsage();
System.exit(1);
}
// each entry should be just one value, but handle multiple paths anyway
for (String p : paths.split(File.pathSeparator)) {
moduleHelpPaths.add(p);
}
}
}
if (moduleHelpPaths.size() == 0) {
errorMessage("Missing molule help path(s) argument - it must be last in the arg list");
errorMessage(
"Missing molule help path(s) arguments - actual arguments:\n\t'" + argList + "'");
printUsage();
System.exit(1);
}
int debugIndex = argList.indexOf(DEBUG_SWITCH);
debugEnabled = debugIndex != -1;
if (!mapped.isEmpty()) {
errorMessage("Ignoring unknown arguments: " + mapped.values());
}
}
private static void errorMessage(String message) {
@ -233,7 +256,7 @@ public class UnusedHelpImageFileFinder {
}
private static void errorMessage(String message, Throwable t) {
System.err.println("[" + GHelpBuilder.class.getSimpleName() + "] " + message);
System.err.println("[" + UnusedHelpImageFileFinder.class.getSimpleName() + "] " + message);
if (t != null) {
t.printStackTrace();
}

View file

@ -0,0 +1,32 @@
/* ###
* IP: GHIDRA
*
* 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 help.validator.links;
import help.validator.model.IMG;
/**
* A link that represents the case where the HTML tried to reference a runtime Java image, but
* that value is not found
*/
public class InvalidRuntimeIMGFileInvalidLink extends InvalidIMGLink {
private static final String MESSAGE =
"Runtime image not found (e.g., Icons.XYZ_ICON not found)";
public InvalidRuntimeIMGFileInvalidLink(IMG img) {
super(img, MESSAGE);
}
}

View file

@ -79,6 +79,10 @@ public class IMG implements Comparable<IMG> {
return imageLocation.isRuntime();
}
public boolean isInvalid() {
return imageLocation.isInvalidRuntimeImage();
}
public Path getImageFile() {
return imgFile;
}