mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +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
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package docking.help;
|
package docking.help;
|
||||||
|
|
||||||
import java.awt.Image;
|
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
@ -25,7 +24,6 @@ import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
import javax.swing.JEditorPane;
|
import javax.swing.JEditorPane;
|
||||||
import javax.swing.event.HyperlinkEvent;
|
import javax.swing.event.HyperlinkEvent;
|
||||||
import javax.swing.event.HyperlinkListener;
|
import javax.swing.event.HyperlinkListener;
|
||||||
|
@ -380,20 +378,10 @@ public class GHelpHTMLEditorKit extends HTMLEditorKit {
|
||||||
*/
|
*/
|
||||||
private class GHelpImageView extends ImageView {
|
private class GHelpImageView extends ImageView {
|
||||||
|
|
||||||
private Image myImage;
|
|
||||||
|
|
||||||
public GHelpImageView(Element elem) {
|
public GHelpImageView(Element elem) {
|
||||||
super(elem);
|
super(elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Image getImage() {
|
|
||||||
if (myImage != null) {
|
|
||||||
return myImage;
|
|
||||||
}
|
|
||||||
return super.getImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getImageURL() {
|
public URL getImageURL() {
|
||||||
|
|
||||||
|
@ -419,10 +407,7 @@ public class GHelpHTMLEditorKit extends HTMLEditorKit {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageIcon icon = iconProvider.getIcon();
|
URL url = iconProvider.getOrCreateUrl();
|
||||||
myImage = icon.getImage();
|
|
||||||
|
|
||||||
URL url = iconProvider.getUrl();
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,17 +15,30 @@
|
||||||
*/
|
*/
|
||||||
package resources;
|
package resources;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
|
import generic.Images;
|
||||||
|
import generic.util.image.ImageUtils;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that knows how to provide an icon and the URL for that icon
|
* A class that knows how to provide an icon and the URL for that icon. If {@link #getUrl()}
|
||||||
|
* returns a non-null value, then that is the URL used to originally load the icon in this class.
|
||||||
|
*
|
||||||
|
* <p>If {@link #getUrl()} returns null, then {@link #getOrCreateUrl()} can be used to create a
|
||||||
|
* value URL by writing out the image for this class's icon.
|
||||||
*/
|
*/
|
||||||
public class IconProvider {
|
public class IconProvider {
|
||||||
|
|
||||||
private ImageIcon icon;
|
private ImageIcon icon;
|
||||||
private URL url;
|
private URL url;
|
||||||
|
private URL tempUrl;
|
||||||
|
private boolean tempFileFailed;
|
||||||
|
|
||||||
public IconProvider(ImageIcon icon, URL url) {
|
public IconProvider(ImageIcon icon, URL url) {
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
|
@ -36,11 +49,76 @@ public class IconProvider {
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInvalid() {
|
||||||
|
return icon == null; // as long as we have an icon, we are valid, url or not
|
||||||
|
}
|
||||||
|
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInvalid() {
|
/**
|
||||||
return icon == null || url == null;
|
* Returns the value of {@link #getUrl()} if it is non-null. Otherwise, this class will
|
||||||
|
* attempt to create a temporary file containing the image of this class in order to return
|
||||||
|
* a URL for that temp file. If a temporary file could not be created, then the URL
|
||||||
|
* returned from this class will point to the
|
||||||
|
* {@link ResourceManager#getDefaultIcon() default icon}.
|
||||||
|
*
|
||||||
|
* @return the URL
|
||||||
|
*/
|
||||||
|
public URL getOrCreateUrl() {
|
||||||
|
if (url != null) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
createTempUrlAsNeeded();
|
||||||
|
return tempUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTempUrlAsNeeded() {
|
||||||
|
if (testUrl(tempUrl)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tempUrl = createTempUrl();
|
||||||
|
if (tempUrl == null) {
|
||||||
|
tempUrl = getDefaultUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private URL createTempUrl() {
|
||||||
|
if (tempFileFailed) {
|
||||||
|
return null; // don't repeatedly attempt to create a temp file
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
File imageFile = File.createTempFile("temp.help.icon", null);
|
||||||
|
imageFile.deleteOnExit(); // don't let this linger
|
||||||
|
ImageUtils.writeFile(icon.getImage(), imageFile);
|
||||||
|
return imageFile.toURI().toURL();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
tempFileFailed = true;
|
||||||
|
Msg.error(this, "Unable to write temp image to display in help for " +
|
||||||
|
ResourceManager.getIconName(icon));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean testUrl(URL testUrl) {
|
||||||
|
if (testUrl == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new File(testUrl.toURI()).exists();
|
||||||
|
}
|
||||||
|
catch (URISyntaxException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private URL getDefaultUrl() {
|
||||||
|
return ResourceManager.getResource(Images.BOMB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ public class Icons {
|
||||||
ResourceManager.loadImage("images/dialog-cancel.png", 10, 10), 6, 6)));
|
ResourceManager.loadImage("images/dialog-cancel.png", 10, 10), 6, 6)));
|
||||||
public static final ImageIcon APPLY_BLOCKED_MATCH_ICON = ResourceManager.getImageIcon(
|
public static final ImageIcon APPLY_BLOCKED_MATCH_ICON = ResourceManager.getImageIcon(
|
||||||
new MultiIcon(ResourceManager.loadImage("images/kgpg.png"), new TranslateIcon(
|
new MultiIcon(ResourceManager.loadImage("images/kgpg.png"), new TranslateIcon(
|
||||||
ResourceManager.loadImage("images/checkmark_green.png", 12, 12), 4, 0)));
|
ResourceManager.loadImage("images/checkmark_green.gif", 12, 12), 4, 0)));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the given string is a Java code snippet that references this class
|
* Returns true if the given string is a Java code snippet that references this class
|
||||||
|
@ -126,24 +126,6 @@ public class Icons {
|
||||||
return new IconProvider(icon, url);
|
return new IconProvider(icon, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a URL for the given code snippet if it is a field reference on this class
|
|
||||||
*
|
|
||||||
* @param snippet the snippet of Java code that references a field of this class
|
|
||||||
* @return the URL; null if the snippet does not refer to a field of this class
|
|
||||||
*/
|
|
||||||
public static URL getUrlForIconsReference(String snippet) {
|
|
||||||
|
|
||||||
String fieldName = getIconName(snippet);
|
|
||||||
if (fieldName == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageIcon icon = getIconByFieldName(fieldName);
|
|
||||||
URL url = getUrlFromIcon(icon);
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getIconName(String snippet) {
|
private static String getIconName(String snippet) {
|
||||||
if (!isIconsReference(snippet)) {
|
if (!isIconsReference(snippet)) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -185,7 +167,7 @@ public class Icons {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
catch (MalformedURLException e) {
|
catch (MalformedURLException e) {
|
||||||
Msg.debug(Icons.class, "Unable to get URL for icon: " + description, e);
|
Msg.trace(Icons.class, "Unable to get URL for icon: " + description);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -160,19 +160,18 @@ public class MultiIcon implements Icon {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
// return getClass().getSimpleName() + "[" + getIconNames() + "]";
|
return getClass().getSimpleName() + "[" + getIconNames() + "]";
|
||||||
return getDescription();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// private String getIconNames() {
|
private String getIconNames() {
|
||||||
// StringBuffer buffy = new StringBuffer();
|
StringBuffer buffy = new StringBuffer();
|
||||||
// for (Icon icon : iconList) {
|
for (Icon icon : iconList) {
|
||||||
// if (buffy.length() > 0) {
|
if (buffy.length() > 0) {
|
||||||
// buffy.append(", ");
|
buffy.append(", ");
|
||||||
// }
|
}
|
||||||
// buffy.append(ResourceManager.getIconName(icon));
|
buffy.append(ResourceManager.getIconName(icon));
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// return buffy.toString();
|
return buffy.toString();
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -400,14 +400,18 @@ public class ResourceManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of this icon. If icon is an ImageIcon, its getDescription() is called to
|
* Get the name of this icon. The value is usually going to be the URL from which the icon
|
||||||
* get the name
|
* was loaded
|
||||||
*
|
*
|
||||||
* @param icon the icon for which the name is desired
|
* @param icon the icon for which the name is desired
|
||||||
* @return the name
|
* @return the name
|
||||||
*/
|
*/
|
||||||
public static String getIconName(Icon icon) {
|
public static String getIconName(Icon icon) {
|
||||||
String iconName = icon.toString();
|
String iconName = icon.toString();
|
||||||
|
|
||||||
|
if (icon instanceof FileBasedIcon) {
|
||||||
|
return ((FileBasedIcon) icon).getFilename();
|
||||||
|
}
|
||||||
if (icon instanceof ImageIcon) {
|
if (icon instanceof ImageIcon) {
|
||||||
iconName = ((ImageIcon) icon).getDescription();
|
iconName = ((ImageIcon) icon).getDescription();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import help.validator.location.*;
|
import help.validator.location.*;
|
||||||
|
import resources.IconProvider;
|
||||||
import resources.Icons;
|
import resources.Icons;
|
||||||
|
|
||||||
public class HelpBuildUtils {
|
public class HelpBuildUtils {
|
||||||
|
@ -536,6 +537,7 @@ public class HelpBuildUtils {
|
||||||
* locate files based upon relative references, specialized help system references (i.e.,
|
* locate files based upon relative references, specialized help system references (i.e.,
|
||||||
* help/topics/...), and absolute URLs.
|
* help/topics/...), and absolute URLs.
|
||||||
*
|
*
|
||||||
|
* @param sourceFile the source file path of the image reference
|
||||||
* @param ref the reference text
|
* @param ref the reference text
|
||||||
* @return an absolute path; null if the URI is remote
|
* @return an absolute path; null if the URI is remote
|
||||||
* @throws URISyntaxException
|
* @throws URISyntaxException
|
||||||
|
@ -544,15 +546,21 @@ public class HelpBuildUtils {
|
||||||
throws URISyntaxException {
|
throws URISyntaxException {
|
||||||
|
|
||||||
if (Icons.isIconsReference(ref)) {
|
if (Icons.isIconsReference(ref)) {
|
||||||
|
|
||||||
// help system syntax: <img src="Icons.ERROR_ICON" />
|
// help system syntax: <img src="Icons.ERROR_ICON" />
|
||||||
URL url = Icons.getUrlForIconsReference(ref);
|
IconProvider iconProvider = Icons.getIconForIconsReference(ref);
|
||||||
if (url == null) {
|
if (iconProvider == null || iconProvider.isInvalid()) {
|
||||||
// bad icon name
|
// bad icon name
|
||||||
return ImageLocation.createInvalidRuntimeLocation(sourceFile, ref);
|
return ImageLocation.createInvalidRuntimeLocation(sourceFile, ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
URI resolved = url.toURI();
|
URL url = iconProvider.getUrl();
|
||||||
Path path = toPath(resolved);
|
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);
|
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
|
* A class that represents the original location of an IMG tag along with its location
|
||||||
* resolution within the help system.
|
* 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 {
|
public class ImageLocation {
|
||||||
|
|
||||||
|
@ -30,8 +33,13 @@ public class ImageLocation {
|
||||||
private Path resolvedPath;
|
private Path resolvedPath;
|
||||||
private URI resolvedUri;
|
private URI resolvedUri;
|
||||||
private boolean isRemote;
|
private boolean isRemote;
|
||||||
|
|
||||||
|
/** An image that is taken from an image loaded by a Java class (e.g., Icons.XYZ_ICON) */
|
||||||
private boolean isRuntime;
|
private boolean isRuntime;
|
||||||
|
|
||||||
|
/** A 'runtime' image that could not be located */
|
||||||
|
private boolean invalidRuntimeImage;
|
||||||
|
|
||||||
public static ImageLocation createLocalLocation(Path sourceFile, String imageSrc,
|
public static ImageLocation createLocalLocation(Path sourceFile, String imageSrc,
|
||||||
URI resolvedUri, Path resolvedPath) {
|
URI resolvedUri, Path resolvedPath) {
|
||||||
|
|
||||||
|
@ -61,6 +69,7 @@ public class ImageLocation {
|
||||||
l.resolvedPath = null;
|
l.resolvedPath = null;
|
||||||
l.isRemote = false;
|
l.isRemote = false;
|
||||||
l.isRuntime = true;
|
l.isRuntime = true;
|
||||||
|
l.invalidRuntimeImage = true;
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,48 +93,28 @@ public class ImageLocation {
|
||||||
return sourceFile;
|
return sourceFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSourceFile(Path sourceFile) {
|
|
||||||
this.sourceFile = sourceFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getImageSrc() {
|
public String getImageSrc() {
|
||||||
return imageSrc;
|
return imageSrc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setImageSrc(String imageSrc) {
|
|
||||||
this.imageSrc = imageSrc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getResolvedPath() {
|
public Path getResolvedPath() {
|
||||||
return resolvedPath;
|
return resolvedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResolvedPath(Path resolvedPath) {
|
|
||||||
this.resolvedPath = resolvedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public URI getResolvedUri() {
|
public URI getResolvedUri() {
|
||||||
return resolvedUri;
|
return resolvedUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResolvedUri(URI resolvedUri) {
|
|
||||||
this.resolvedUri = resolvedUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRemote() {
|
public boolean isRemote() {
|
||||||
return isRemote;
|
return isRemote;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRemote(boolean isRemote) {
|
|
||||||
this.isRemote = isRemote;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRuntime() {
|
public boolean isRuntime() {
|
||||||
return isRuntime;
|
return isRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRuntime(boolean isRuntime) {
|
public boolean isInvalidRuntimeImage() {
|
||||||
this.isRuntime = isRuntime;
|
return invalidRuntimeImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -120,14 +120,22 @@ public class JavaHelpValidator {
|
||||||
return; // don't even try to verify a remote URL
|
return; // don't even try to verify a remote URL
|
||||||
}
|
}
|
||||||
|
|
||||||
Path imagePath = img.getImageFile();
|
if (img.isRuntime()) {
|
||||||
if (imagePath == null) {
|
|
||||||
unresolvedLinks.add(new NonExistentIMGFileInvalidLink(img));
|
//
|
||||||
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (img.isRuntime()) {
|
Path imagePath = img.getImageFile();
|
||||||
// the tool will load this image at runtime--don't perform normal validate
|
if (imagePath == null) {
|
||||||
|
unresolvedLinks.add(new NonExistentIMGFileInvalidLink(img));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,22 +15,25 @@
|
||||||
*/
|
*/
|
||||||
package help.validator;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.*;
|
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 {
|
public class UnusedHelpImageFileFinder {
|
||||||
|
|
||||||
|
private static final String HELP_PATHS_OPTION = "-hp"; // taken from GHelpBuilder
|
||||||
private static final String DEBUG_SWITCH = "-debug";
|
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 static boolean debugEnabled = false;
|
||||||
|
|
||||||
private SortedSet<Path> unusedFiles;
|
private SortedSet<Path> unusedFiles;
|
||||||
|
@ -78,24 +80,20 @@ public class UnusedHelpImageFileFinder {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SortedSet<Path> getUnusedImages() {
|
public SortedSet<Path> getUnusedImages() {
|
||||||
return new TreeSet<Path>(unusedFiles);
|
return new TreeSet<>(unusedFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SortedSet<Path> getUnusedFiles(Collection<IMG> referencedIMGs,
|
private static SortedSet<Path> getUnusedFiles(Collection<IMG> referencedIMGs,
|
||||||
Collection<Path> imageFiles) {
|
Collection<Path> imageFiles) {
|
||||||
|
|
||||||
Map<Path, IMG> fileToIMGMap = new HashMap<Path, IMG>();
|
Map<Path, IMG> fileToIMGMap = new HashMap<>();
|
||||||
for (IMG img : referencedIMGs) {
|
for (IMG img : referencedIMGs) {
|
||||||
fileToIMGMap.put(img.getImageFile(), img);
|
fileToIMGMap.put(img.getImageFile(), img);
|
||||||
}
|
}
|
||||||
|
|
||||||
SortedSet<Path> set = new TreeSet<Path>(new Comparator<Path>() {
|
SortedSet<Path> set =
|
||||||
@Override
|
new TreeSet<>((f1, f2) -> f1.toUri().toString().toLowerCase().compareTo(
|
||||||
public int compare(Path f1, Path f2) {
|
f2.toUri().toString().toLowerCase()));
|
||||||
return f1.toUri().toString().toLowerCase().compareTo(
|
|
||||||
f2.toUri().toString().toLowerCase());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
for (Path file : imageFiles) {
|
for (Path file : imageFiles) {
|
||||||
IMG img = fileToIMGMap.get(file);
|
IMG img = fileToIMGMap.get(file);
|
||||||
if (img == null && !isExcludedImageFile(file)) {
|
if (img == null && !isExcludedImageFile(file)) {
|
||||||
|
@ -111,8 +109,9 @@ public class UnusedHelpImageFileFinder {
|
||||||
return absolutePath.indexOf("help/shared/") != -1;
|
return absolutePath.indexOf("help/shared/") != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Collection<IMG> getReferencedIMGs(Collection<HelpModuleLocation> helpCollections) {
|
private static Collection<IMG> getReferencedIMGs(
|
||||||
Set<IMG> set = new HashSet<IMG>();
|
Collection<HelpModuleLocation> helpCollections) {
|
||||||
|
Set<IMG> set = new HashSet<>();
|
||||||
for (HelpModuleLocation help : helpCollections) {
|
for (HelpModuleLocation help : helpCollections) {
|
||||||
Collection<IMG> IMGs = help.getAllIMGs();
|
Collection<IMG> IMGs = help.getAllIMGs();
|
||||||
set.addAll(IMGs);
|
set.addAll(IMGs);
|
||||||
|
@ -122,7 +121,7 @@ public class UnusedHelpImageFileFinder {
|
||||||
|
|
||||||
private static Collection<Path> getAllImagesOnDisk(
|
private static Collection<Path> getAllImagesOnDisk(
|
||||||
Collection<HelpModuleLocation> helpDirectories) {
|
Collection<HelpModuleLocation> helpDirectories) {
|
||||||
List<Path> files = new ArrayList<Path>();
|
List<Path> files = new ArrayList<>();
|
||||||
for (HelpModuleLocation help : helpDirectories) {
|
for (HelpModuleLocation help : helpDirectories) {
|
||||||
Path helpDir = help.getHelpLocation();
|
Path helpDir = help.getHelpLocation();
|
||||||
gatherImageFiles(helpDir, files);
|
gatherImageFiles(helpDir, files);
|
||||||
|
@ -155,10 +154,10 @@ public class UnusedHelpImageFileFinder {
|
||||||
|
|
||||||
private static List<HelpModuleLocation> collectHelp() {
|
private static List<HelpModuleLocation> collectHelp() {
|
||||||
debug("Parsing help dirs...");
|
debug("Parsing help dirs...");
|
||||||
List<HelpModuleLocation> helpCollections =
|
List<HelpModuleLocation> helpCollections = new ArrayList<>(moduleHelpPaths.size());
|
||||||
new ArrayList<HelpModuleLocation>(moduleHelpPaths.size());
|
|
||||||
for (String helpDirName : moduleHelpPaths) {
|
for (String helpDirName : moduleHelpPaths) {
|
||||||
// 1) Make sure the help directory exists
|
|
||||||
|
// Make sure the help directory exists
|
||||||
File helpDirectoryFile = null;
|
File helpDirectoryFile = null;
|
||||||
try {
|
try {
|
||||||
helpDirectoryFile = new File(helpDirName).getCanonicalFile();
|
helpDirectoryFile = new File(helpDirName).getCanonicalFile();
|
||||||
|
@ -172,14 +171,8 @@ public class UnusedHelpImageFileFinder {
|
||||||
errorMessage("Help directory not found - skipping: " + helpDirName);
|
errorMessage("Help directory not found - skipping: " + helpDirName);
|
||||||
continue;
|
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));
|
helpCollections.add(HelpBuildUtils.toLocation(helpDirectoryFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,8 +181,8 @@ public class UnusedHelpImageFileFinder {
|
||||||
|
|
||||||
private static void debug(String string) {
|
private static void debug(String string) {
|
||||||
if (debugEnabled) {
|
if (debugEnabled) {
|
||||||
System.out.println("[" + UnusedHelpImageFileFinder.class.getSimpleName() + "] " +
|
System.out.println(
|
||||||
string);
|
"[" + UnusedHelpImageFileFinder.class.getSimpleName() + "] " + string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +190,7 @@ public class UnusedHelpImageFileFinder {
|
||||||
StringBuilder buffy = new StringBuilder();
|
StringBuilder buffy = new StringBuilder();
|
||||||
|
|
||||||
errorMessage("Usage:\n");
|
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());
|
errorMessage(buffy.toString());
|
||||||
}
|
}
|
||||||
|
@ -209,23 +202,53 @@ public class UnusedHelpImageFileFinder {
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> argList = Arrays.asList(args);
|
List<String> argList = CollectionUtils.asList(args);
|
||||||
|
int debugIndex = argList.indexOf(DEBUG_SWITCH);
|
||||||
// get module directory paths
|
if (debugIndex > -1) {
|
||||||
String modulePathsString = argList.get(args.length - 1);
|
debugEnabled = true;
|
||||||
moduleHelpPaths = new ArrayList<String>();
|
argList.remove(debugIndex);
|
||||||
StringTokenizer tokenizer = new StringTokenizer(modulePathsString, File.pathSeparator);
|
|
||||||
while (tokenizer.hasMoreTokens()) {
|
|
||||||
moduleHelpPaths.add(tokenizer.nextToken());
|
|
||||||
}
|
}
|
||||||
if (moduleHelpPaths.size() == 0) {
|
|
||||||
errorMessage("Missing molule help path(s) argument - it must be last in the arg list");
|
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();
|
printUsage();
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int debugIndex = argList.indexOf(DEBUG_SWITCH);
|
mapped.remove(i);
|
||||||
debugEnabled = debugIndex != -1;
|
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) arguments - actual arguments:\n\t'" + argList + "'");
|
||||||
|
printUsage();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mapped.isEmpty()) {
|
||||||
|
errorMessage("Ignoring unknown arguments: " + mapped.values());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void errorMessage(String message) {
|
private static void errorMessage(String message) {
|
||||||
|
@ -233,7 +256,7 @@ public class UnusedHelpImageFileFinder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void errorMessage(String message, Throwable t) {
|
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) {
|
if (t != null) {
|
||||||
t.printStackTrace();
|
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();
|
return imageLocation.isRuntime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInvalid() {
|
||||||
|
return imageLocation.isInvalidRuntimeImage();
|
||||||
|
}
|
||||||
|
|
||||||
public Path getImageFile() {
|
public Path getImageFile() {
|
||||||
return imgFile;
|
return imgFile;
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,38 @@ task buildHelp(type: JavaExec, dependsOn: indexHelp) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Task for finding unused images that are not referenced from Ghidra help files
|
||||||
|
task findUnusedHelp(type: JavaExec) {
|
||||||
|
group rootProject.GHIDRA_GROUP
|
||||||
|
description " Finds unused help images for this module. [gradle/helpProject.gradle]\n"
|
||||||
|
|
||||||
|
File helpRootDir = file('src/main/help/help')
|
||||||
|
File outputDir = file('build/help/main/help')
|
||||||
|
|
||||||
|
dependsOn configurations.helpPath
|
||||||
|
|
||||||
|
inputs.dir helpRootDir
|
||||||
|
|
||||||
|
classpath = sourceSets.helpIndex.runtimeClasspath
|
||||||
|
|
||||||
|
main = 'help.validator.UnusedHelpImageFileFinder'
|
||||||
|
|
||||||
|
args '-debug' // print debug info
|
||||||
|
|
||||||
|
doFirst {
|
||||||
|
// this modules runtime classpath (contains jhall.jar)
|
||||||
|
classpath project(':Help').sourceSets.main.runtimeClasspath
|
||||||
|
|
||||||
|
// the current help dir to process
|
||||||
|
args "-hp"
|
||||||
|
args "${helpRootDir.absolutePath}"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// include the help into the module's jar
|
// include the help into the module's jar
|
||||||
jar {
|
jar {
|
||||||
from "build/help/main" // include the generated help index files
|
from "build/help/main" // include the generated help index files
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue