mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
Help - Added a task to find unused help images; fixed showing in-memory
images in help
This commit is contained in:
parent
e8dcb3d0c4
commit
49436c44a9
12 changed files with 275 additions and 131 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -79,6 +79,10 @@ public class IMG implements Comparable<IMG> {
|
|||
return imageLocation.isRuntime();
|
||||
}
|
||||
|
||||
public boolean isInvalid() {
|
||||
return imageLocation.isInvalidRuntimeImage();
|
||||
}
|
||||
|
||||
public Path getImageFile() {
|
||||
return imgFile;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue