more scrubbing before redoing ContributionManagerDialog

This commit is contained in:
Ben Fry
2015-09-22 08:30:24 -04:00
parent 8c522056de
commit d5f39ee20e
7 changed files with 190 additions and 201 deletions
@@ -36,20 +36,22 @@ import processing.data.StringDict;
public class ContributionListing {
// Stable URL that will redirect to wherever we're hosting the file
static volatile ContributionListing singleInstance;
/** Stable URL that will redirect to wherever the file is hosted */
static final String LISTING_URL = "http://download.processing.org/contribs";
static final String LOCAL_FILENAME = "contribs.txt";
static volatile ContributionListing singleInstance;
/** Location of the listing file on disk, will be read and written. */
File listingFile;
List<ChangeListener> listeners;
List<AvailableContribution> advertisedContributions;
Map<String, List<Contribution>> librariesByCategory;
public Map<String, Contribution> librariesByImportHeader;
Map<String, Contribution> librariesByImportHeader;
List<Contribution> allContributions;
boolean hasDownloadedLatestList;
boolean hasListDownloadFailed;
boolean listDownloaded;
boolean listDownloadFailed;
ReentrantLock downloadingListingLock;
@@ -70,7 +72,7 @@ public class ContributionListing {
}
public static ContributionListing getInstance() {
static public ContributionListing getInstance() {
if (singleInstance == null) {
synchronized (ContributionListing.class) {
if (singleInstance == null) {
@@ -82,7 +84,7 @@ public class ContributionListing {
}
void setAdvertisedList(File file) {
private void setAdvertisedList(File file) {
listingFile = file;
advertisedContributions.clear();
@@ -127,8 +129,8 @@ public class ContributionListing {
if (oldLib.getImports() != null) {
for (String importName : oldLib.getImports()) {
if (librariesByImportHeader.containsKey(importName)) {
librariesByImportHeader.put(importName, newLib);
if (getLibrariesByImportHeader().containsKey(importName)) {
getLibrariesByImportHeader().put(importName, newLib);
}
}
}
@@ -147,7 +149,7 @@ public class ContributionListing {
private void addContribution(Contribution contribution) {
if (contribution.getImports() != null) {
for (String importName : contribution.getImports()) {
librariesByImportHeader.put(importName, contribution);
getLibrariesByImportHeader().put(importName, contribution);
}
}
for (String category : contribution.getCategories()) {
@@ -176,7 +178,7 @@ public class ContributionListing {
}
if (contribution.getImports() != null) {
for (String importName : contribution.getImports()) {
librariesByImportHeader.remove(importName);
getLibrariesByImportHeader().remove(importName);
}
}
allContributions.remove(contribution);
@@ -196,9 +198,7 @@ public class ContributionListing {
protected AvailableContribution getAvailableContribution(Contribution info) {
Iterator<AvailableContribution> iter = advertisedContributions.iterator();
while(iter.hasNext()) {
AvailableContribution advertised = iter.next();
for (AvailableContribution advertised : advertisedContributions) {
if (advertised.getType() == info.getType() &&
advertised.getName().equals(info.getName())) {
return advertised;
@@ -217,7 +217,7 @@ public class ContributionListing {
if (filter.matches(contrib)) {
// TODO still not sure why category would be coming back null [fry]
// http://code.google.com/p/processing/issues/detail?id=1387
if (categoryName != null && categoryName.trim().length() != 0) {
if (categoryName != null && !categoryName.trim().isEmpty()) {
outgoing.add(categoryName);
}
break;
@@ -264,11 +264,11 @@ public class ContributionListing {
}
private boolean matches(Contribution contrib, String filter) {
int colon = filter.indexOf(":");
private boolean matches(Contribution contrib, String typed) {
int colon = typed.indexOf(":");
if (colon != -1) {
String isText = filter.substring(0, colon);
String property = filter.substring(colon + 1);
String isText = typed.substring(0, colon);
String property = typed.substring(colon + 1);
// Chances are the person is still typing the property, so rather than
// make the list flash empty (because nothing contains "is:" or "has:",
@@ -278,28 +278,37 @@ public class ContributionListing {
}
if ("is".equals(isText) || "has".equals(isText)) {
return hasProperty(contrib, filter.substring(colon + 1));
return hasProperty(contrib, typed.substring(colon + 1));
} else if ("not".equals(isText)) {
return !hasProperty(contrib, filter.substring(colon + 1));
return !hasProperty(contrib, typed.substring(colon + 1));
}
}
filter = ".*" + filter.toLowerCase() + ".*";
typed = ".*" + typed.toLowerCase() + ".*";
return contrib.getAuthorList() != null && deAccent(contrib.getAuthorList().toLowerCase()).matches(filter)
|| contrib.getSentence() != null && deAccent(contrib.getSentence().toLowerCase()).matches(filter)
|| contrib.getParagraph() != null && deAccent(contrib.getParagraph().toLowerCase()).matches(filter)
|| contrib.hasCategory(filter)
|| contrib.getName() != null && deAccent(contrib.getName().toLowerCase()).matches(filter);
return (matchField(contrib.getName(), typed) ||
matchField(contrib.getAuthorList(), typed) ||
matchField(contrib.getSentence(), typed) ||
matchField(contrib.getParagraph(), typed) ||
contrib.hasCategory(typed));
}
public String deAccent(String str) {
static private boolean matchField(String field, String typed) {
return (field != null) &&
removeAccents(field.toLowerCase()).matches(typed);
}
// TODO is this removing characters with accents, not ascii normalizing them? [fry]
static private String removeAccents(String str) {
String nfdNormalizedString = Normalizer.normalize(str, Normalizer.Form.NFD);
Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
return pattern.matcher(nfdNormalizedString).replaceAll("");
}
private boolean isProperty(String property) {
static private boolean isProperty(String property) {
return property.startsWith("updat") || property.startsWith("upgrad")
|| property.startsWith("instal") && !property.startsWith("installabl")
|| property.equals("tool") || property.startsWith("lib")
@@ -324,20 +333,15 @@ public class ContributionListing {
}
if (property.startsWith("lib")) {
return contrib.getType() == ContributionType.LIBRARY;
// return contrib.getType() == Contribution.Type.LIBRARY
// || contrib.getType() == Contribution.Type.LIBRARY_COMPILATION;
}
if (property.equals("mode")) {
return contrib.getType() == ContributionType.MODE;
}
// if (property.equals("compilation")) {
// return contrib.getType() == Contribution.Type.LIBRARY_COMPILATION;
// }
return false;
}
/*
protected List<Contribution> listCompatible(List<Contribution> contribs, boolean filter) {
List<Contribution> filteredList =
new ArrayList<Contribution>(contribs);
@@ -353,6 +357,7 @@ public class ContributionListing {
}
return filteredList;
}
*/
private void notifyRemove(Contribution contribution) {
@@ -376,7 +381,7 @@ public class ContributionListing {
}
protected void addContributionListener(ChangeListener listener) {
protected void addListener(ChangeListener listener) {
for (Contribution contrib : allContributions) {
listener.contributionAdded(contrib);
}
@@ -384,18 +389,6 @@ public class ContributionListing {
}
/*
private void removeContributionListener(ContributionChangeListener listener) {
listeners.remove(listener);
}
private ArrayList<ContributionChangeListener> getContributionListeners() {
return new ArrayList<ContributionChangeListener>(listeners);
}
*/
/**
* Starts a new thread to download the advertised list of contributions.
* Only one instance will run at a time.
@@ -430,11 +423,11 @@ public class ContributionListing {
listingFile.delete(); // may silently fail, but below may still work
}
if (tempContribFile.renameTo(listingFile)) {
hasDownloadedLatestList = true;
hasListDownloadFailed = false;
listDownloaded = true;
listDownloadFailed = false;
setAdvertisedList(listingFile);
} else {
hasListDownloadFailed = true;
listDownloadFailed = true;
}
}
@@ -449,9 +442,19 @@ public class ContributionListing {
/*
boolean hasUpdates() {
for (Contribution info : allContributions) {
if (hasUpdates(info)) {
boolean hasUpdates(Base base) {
for (ModeContribution mc : base.getModeContribs()) {
if (hasUpdates(mc)) {
return true;
}
}
for (Library lib : base.getActiveEditor().getMode().contribLibraries) {
if (hasUpdates(lib)) {
return true;
}
}
for (ToolContribution tc : base.getToolContribs()) {
if (hasUpdates(tc)) {
return true;
}
}
@@ -460,6 +463,80 @@ public class ContributionListing {
*/
protected boolean hasUpdates(Contribution contribution) {
if (contribution.isInstalled()) {
Contribution advertised = getAvailableContribution(contribution);
if (advertised == null) {
return false;
}
return advertised.getVersion() > contribution.getVersion()
&& advertised.isCompatible(Base.getRevision());
}
return false;
}
protected String getLatestVersion(Contribution contribution) {
Contribution newestContrib = getAvailableContribution(contribution);
if (newestContrib == null) {
return null;
}
return newestContrib.getPrettyVersion();
}
protected boolean hasDownloadedLatestList() {
return listDownloaded;
}
protected boolean hasListDownloadFailed() {
return listDownloadFailed;
}
private List<AvailableContribution> parseContribList(File file) {
List<AvailableContribution> outgoing =
new ArrayList<AvailableContribution>();
if (file != null && file.exists()) {
String[] lines = PApplet.loadStrings(file);
int start = 0;
while (start < lines.length) {
String type = lines[start];
ContributionType contribType = ContributionType.fromName(type);
if (contribType == null) {
System.err.println("Error in contribution listing file on line " + (start+1));
// Scan forward for the next blank line
int end = ++start;
while (end < lines.length && !lines[end].trim().isEmpty()) {
end++;
}
start = end + 1;
} else {
// Scan forward for the next blank line
int end = ++start;
while (end < lines.length && !lines[end].trim().isEmpty()) {
end++;
}
String[] contribLines = PApplet.subset(lines, start, end-start);
StringDict contribParams = Util.readSettings(file.getName(), contribLines);
outgoing.add(new AvailableContribution(contribType, contribParams));
start = end + 1;
}
}
}
return outgoing;
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/**
* TODO This needs to be called when the listing loads, and also whenever
* the contribs list has been updated (for whatever reason). In addition,
@@ -467,7 +544,7 @@ public class ContributionListing {
* correct information on the number of items available.
* @return The number of contributions that have available updates.
*/
int countUpdates(Base base) {
public int countUpdates(Base base) {
int count = 0;
for (ModeContribution mc : base.getModeContribs()) {
if (hasUpdates(mc)) {
@@ -488,106 +565,12 @@ public class ContributionListing {
}
boolean hasUpdates(Base base) {
for (ModeContribution mc : base.getModeContribs()) {
if (hasUpdates(mc)) {
return true;
}
}
for (Library lib : base.getActiveEditor().getMode().contribLibraries) {
if (hasUpdates(lib)) {
return true;
}
}
for (ToolContribution tc : base.getToolContribs()) {
if (hasUpdates(tc)) {
return true;
}
}
return false;
/** Used by JavaEditor to auto-import */
public Map<String, Contribution> getLibrariesByImportHeader() {
return librariesByImportHeader;
}
boolean hasUpdates(Contribution contribution) {
if (contribution.isInstalled()) {
Contribution advertised = getAvailableContribution(contribution);
if (advertised == null) {
return false;
}
return advertised.getVersion() > contribution.getVersion()
&& advertised.isCompatible(Base.getRevision());
}
return false;
}
String getLatestVersion(Contribution contribution) {
Contribution newestContrib = getAvailableContribution(contribution);
if (newestContrib == null) {
return null;
}
return newestContrib.getPrettyVersion();
}
boolean hasDownloadedLatestList() {
return hasDownloadedLatestList;
}
boolean hasListDownloadFailed() {
return hasListDownloadFailed;
}
List<AvailableContribution> parseContribList(File file) {
List<AvailableContribution> outgoing = new ArrayList<AvailableContribution>();
if (file != null && file.exists()) {
String[] lines = PApplet.loadStrings(file);
int start = 0;
while (start < lines.length) {
String type = lines[start];
ContributionType contribType = ContributionType.fromName(type);
if (contribType == null) {
System.err.println("Error in contribution listing file on line " + (start+1));
// Scan forward for the next blank line
int end = ++start;
while (end < lines.length && lines[end].trim().length() != 0) {
end++;
}
start = end + 1;
} else {
// Scan forward for the next blank line
int end = ++start;
while (end < lines.length && lines[end].trim().length() != 0) {
end++;
}
String[] contribLines = PApplet.subset(lines, start, end-start);
StringDict contribParams = Util.readSettings(file.getName(), contribLines);
outgoing.add(new AvailableContribution(contribType, contribParams));
start = end + 1;
}
}
}
return outgoing;
}
// boolean isDownloadingListing() {
// return downloadingListingLock.isLocked();
// }
// public Comparator<? super Contribution> getComparator() {
// return COMPARATOR;
// }
static public Comparator<Contribution> COMPARATOR = new Comparator<Contribution>() {
public int compare(Contribution o1, Contribution o2) {
return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase());
@@ -56,11 +56,11 @@ public class ContributionManagerDialog {
// the calling editor, so updates can be applied
Editor editor;
ContributionTab toolsContributionTab;
ContributionTab librariesContributionTab;
ContributionTab examplesContributionTab;
ContributionTab modesContributionTab;
UpdateContributionTab updatesContributionTab;
ContributionTab librariesTab;
ContributionTab modesTab;
ContributionTab toolsTab;
ContributionTab examplesTab;
UpdateContributionTab updatesTab;
JLabel numberLabel;
ContributionListing contributionListing = ContributionListing.getInstance();
@@ -69,17 +69,14 @@ public class ContributionManagerDialog {
private JPanel updateTabPanel;
private JLabel updateTabLabel;
static Font font;
public ContributionManagerDialog() {
font = Toolkit.getSansFont(14, Font.PLAIN);
numberLabel = new JLabel(Toolkit.getLibIconX("manager/notification"));
librariesContributionTab = new ContributionTab(ContributionType.LIBRARY, this);
modesContributionTab = new ContributionTab(ContributionType.MODE, this);
toolsContributionTab = new ContributionTab(ContributionType.TOOL, this);
examplesContributionTab = new ContributionTab(ContributionType.EXAMPLES, this);
updatesContributionTab = new UpdateContributionTab(null, this);
librariesTab = new ContributionTab(ContributionType.LIBRARY, this);
modesTab = new ContributionTab(ContributionType.MODE, this);
toolsTab = new ContributionTab(ContributionType.TOOL, this);
examplesTab = new ContributionTab(ContributionType.EXAMPLES, this);
updatesTab = new UpdateContributionTab(null, this);
}
@@ -109,17 +106,17 @@ public class ContributionManagerDialog {
}
ContributionTab getTab(ContributionType contributionType) {
protected ContributionTab getTab(ContributionType contributionType) {
if (contributionType == ContributionType.LIBRARY) {
return librariesContributionTab;
return librariesTab;
} else if (contributionType == ContributionType.MODE) {
return modesContributionTab;
return modesTab;
} else if (contributionType == ContributionType.TOOL) {
return toolsContributionTab;
return toolsTab;
} else if (contributionType == ContributionType.EXAMPLES) {
return examplesContributionTab;
return examplesTab;
}
return updatesContributionTab;
return updatesTab;
}
@@ -130,19 +127,19 @@ public class ContributionManagerDialog {
makeAndShowTab(false, true);
tabbedPane.addTab("Libraries", null, librariesContributionTab, "Libraries");
tabbedPane.addTab("Libraries", null, librariesTab, "Libraries");
tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);
tabbedPane.addTab("Modes", null, modesContributionTab, "Modes");
tabbedPane.addTab("Modes", null, modesTab, "Modes");
tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);
tabbedPane.addTab("Tools", null, toolsContributionTab, "Tools");
tabbedPane.addTab("Tools", null, toolsTab, "Tools");
tabbedPane.setMnemonicAt(2, KeyEvent.VK_3);
tabbedPane.addTab("Examples", null, examplesContributionTab, "Examples");
tabbedPane.addTab("Examples", null, examplesTab, "Examples");
tabbedPane.setMnemonicAt(3, KeyEvent.VK_4);
tabbedPane.addTab("Updates", null, updatesContributionTab, "Updates");
tabbedPane.addTab("Updates", null, updatesTab, "Updates");
tabbedPane.setMnemonicAt(4, KeyEvent.VK_5);
tabbedPane.setUI(new SpacedTabbedPaneUI());
@@ -476,26 +473,26 @@ public class ContributionManagerDialog {
void makeAndShowTab(boolean activateErrorPanel, boolean isLoading) {
librariesContributionTab.showFrame(editor, activateErrorPanel, isLoading);
modesContributionTab.showFrame(editor, activateErrorPanel, isLoading);
toolsContributionTab.showFrame(editor, activateErrorPanel, isLoading);
examplesContributionTab.showFrame(editor, activateErrorPanel, isLoading);
updatesContributionTab.showFrame(editor, activateErrorPanel, isLoading);
librariesTab.showFrame(editor, activateErrorPanel, isLoading);
modesTab.showFrame(editor, activateErrorPanel, isLoading);
toolsTab.showFrame(editor, activateErrorPanel, isLoading);
examplesTab.showFrame(editor, activateErrorPanel, isLoading);
updatesTab.showFrame(editor, activateErrorPanel, isLoading);
}
ContributionTab getActiveTab() {
switch (tabbedPane.getSelectedIndex()) {
case 0:
return librariesContributionTab;
return librariesTab;
case 1:
return modesContributionTab;
return modesTab;
case 2:
return toolsContributionTab;
return toolsTab;
case 3:
return examplesContributionTab;
return examplesTab;
default:
return updatesContributionTab;
return updatesTab;
}
}
}
@@ -100,7 +100,7 @@ public class ContributionTab extends JPanel {
contribListing = ContributionListing.getInstance();
statusPanel = new StatusPanel(650, this);
contributionListPanel = new ListPanel(this, filter);
contribListing.addContributionListener(contributionListPanel);
contribListing.addListener(contributionListPanel);
}
+12 -5
View File
@@ -57,6 +57,10 @@ implements Scrollable, ContributionListing.ChangeListener {
static Icon upToDateIcon;
static Icon updateAvailableIcon;
static Icon incompatibleIcon;
static Icon foundationIcon;
static Font plainFont;
static Font boldFont;
public ListPanel() {
@@ -64,6 +68,10 @@ implements Scrollable, ContributionListing.ChangeListener {
upToDateIcon = Toolkit.getLibIconX("manager/up-to-date");
updateAvailableIcon = Toolkit.getLibIconX("manager/update-available");
incompatibleIcon = Toolkit.getLibIconX("manager/incompatible");
foundationIcon = Toolkit.getLibIconX("icons/foundation", 16);
plainFont = Toolkit.getSansFont(14, Font.PLAIN);
boldFont = Toolkit.getSansFont(14, Font.BOLD);
}
}
@@ -103,7 +111,7 @@ implements Scrollable, ContributionListing.ChangeListener {
table.setFillsViewportHeight(true);
// table.setBorder();
table.setDefaultRenderer(Contribution.class, new ContribStatusRenderer());
table.setFont(Toolkit.getSansFont(14, Font.PLAIN));
table.setFont(plainFont);
table.setRowHeight(28);
table.setRowMargin(6);
table.getColumnModel().setColumnMargin(0);
@@ -294,7 +302,7 @@ implements Scrollable, ContributionListing.ChangeListener {
}
if (column == 0) {
Icon icon = null;
label.setFont(Toolkit.getSansFont(14, Font.PLAIN));
label.setFont(plainFont);
if (contribution.isInstalled()) {
icon = upToDateIcon;
if (contribListing.hasUpdates(contribution)) {
@@ -314,7 +322,6 @@ implements Scrollable, ContributionListing.ChangeListener {
} else if (column == 1) {
// Generating ellipses based on fontMetrics
Font boldFont = Toolkit.getSansFont(14, Font.BOLD);
String fontFace = "<font face=\"" + boldFont.getName() + "\">";
FontMetrics fontMetrics = table.getFontMetrics(boldFont); //table.getFont());
int colSize = table.getColumnModel().getColumn(1).getWidth();
@@ -349,11 +356,11 @@ implements Scrollable, ContributionListing.ChangeListener {
if (table.isRowSelected(row)) {
label.setBackground(new Color(0xe0fffd));
}
label.setFont(ContributionManagerDialog.font);
label.setFont(plainFont);
label.setOpaque(true);
} else {
if (contribution.isSpecial()) {
label = new JLabel(Toolkit.getLibIconX("icons/foundation", 16));
label = new JLabel(foundationIcon);
} else {
label = new JLabel();
}
@@ -91,8 +91,8 @@ class StatusPanel extends JPanel {
label.setEditable(false);
label.setOpaque(false);
label.setContentType("text/html");
bodyRule = "a, body { font-family: " + ContributionManagerDialog.font.getFamily() + "; " +
"font-size: " + ContributionManagerDialog.font.getSize() + "pt; color: black; text-decoration: none;}";
bodyRule = "a, body { font-family: " + buttonFont.getFamily() + "; " +
"font-size: " + buttonFont.getSize() + "pt; color: black; text-decoration: none;}";
label.addHyperlinkListener(new HyperlinkListener() {
@Override
@@ -29,7 +29,7 @@ public class UpdateContributionTab extends ContributionTab {
this.contributionType = type;
this.contributionManagerDialog = contributionManagerDialog;
contribListing = ContributionListing.getInstance();
contribListing.addContributionListener(contributionListPanel);
contribListing.addListener(contributionListPanel);
}
@@ -1924,16 +1924,18 @@ public class JavaEditor extends Editor {
* @param importHeaders
*/
private List<AvailableContribution> getNotInstalledAvailableLibs(ArrayList<String> importHeadersList) {
Map<String, Contribution> importMap = ContributionListing.getInstance().librariesByImportHeader;
ArrayList<AvailableContribution> libList = new ArrayList<AvailableContribution>();
Map<String, Contribution> importMap =
ContributionListing.getInstance().getLibrariesByImportHeader();
List<AvailableContribution> libList = new ArrayList<AvailableContribution>();
for (String importHeaders : importHeadersList) {
int dot = importHeaders.lastIndexOf('.');
String entry = (dot == -1) ? importHeaders : importHeaders.substring(0,
dot);
if (entry.startsWith("java.") || entry.startsWith("javax.")
|| entry.startsWith("processing.")) {
continue;// null;
if (entry.startsWith("java.") ||
entry.startsWith("javax.") ||
entry.startsWith("processing.")) {
continue;
}
Library library = null;