From fc887f32647c0f10994de82553fb58f651842ab1 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 25 Jan 2017 15:34:14 +0100 Subject: [PATCH 01/12] Improved lib equality check --- .../contributions/libraries/ContributedLibrary.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java index 3aa1198882c..0c723ae3e17 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -166,7 +166,7 @@ public boolean equals(Object obj) { String thisVersion = getParsedVersion(); String otherVersion = other.getParsedVersion(); - boolean versionEquals = (thisVersion != null && otherVersion != null + boolean versionEquals = (thisVersion != null && thisVersion.equals(otherVersion)); // Important: for legacy libs, versions are null. Two legacy libs must @@ -176,9 +176,14 @@ public boolean equals(Object obj) { String thisName = getName(); String otherName = other.getName(); - - boolean nameEquals = thisName == null || otherName == null || thisName.equals(otherName); + boolean nameEquals = thisName != null && thisName.equals(otherName); return versionEquals && nameEquals; } + + @Override + public int hashCode() { + String hashingData = "CONTRIBUTEDLIB" + getName() + getVersion(); + return hashingData.hashCode(); + } } From 4616de02494311ab9ba50cced902444267a60c1b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 25 Jan 2017 17:21:50 +0100 Subject: [PATCH 02/12] Renamed ContributedLibraryReference to ContributedLibraryDependency --- .../contributions/libraries/ContributedLibrary.java | 4 ++-- ...ryReference.java => ContributedLibraryDependency.java} | 8 +++----- arduino-core/src/processing/app/packages/UserLibrary.java | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) rename arduino-core/src/cc/arduino/contributions/libraries/{ContributedLibraryReference.java => ContributedLibraryDependency.java} (87%) diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java index 0c723ae3e17..485d6183bb3 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -63,7 +63,7 @@ public abstract class ContributedLibrary extends DownloadableContribution { public abstract List getTypes(); - public abstract List getRequires(); + public abstract List getRequires(); public abstract List getProvidesIncludes(); @@ -146,7 +146,7 @@ public String info() { res += "\n"; res += " requires :\n"; if (getRequires() != null) - for (ContributedLibraryReference r : getRequires()) { + for (ContributedLibraryDependency r : getRequires()) { res += " " + r; } res += "\n"; diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibraryReference.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibraryDependency.java similarity index 87% rename from arduino-core/src/cc/arduino/contributions/libraries/ContributedLibraryReference.java rename to arduino-core/src/cc/arduino/contributions/libraries/ContributedLibraryDependency.java index f4edd57327f..2683d5eb046 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibraryReference.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibraryDependency.java @@ -29,16 +29,14 @@ package cc.arduino.contributions.libraries; -public abstract class ContributedLibraryReference { +public abstract class ContributedLibraryDependency { public abstract String getName(); - public abstract String getMaintainer(); - - public abstract String getVersion(); + public abstract String getVersionRequired(); @Override public String toString() { - return getName() + " " + getVersion() + " (" + getMaintainer() + ")"; + return getName() + " " + getVersionRequired(); } } diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index e22ae36eb29..c1625b88a01 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -44,7 +44,7 @@ import cc.arduino.Constants; import cc.arduino.contributions.VersionHelper; -import cc.arduino.contributions.libraries.ContributedLibraryReference; +import cc.arduino.contributions.libraries.ContributedLibraryDependency; import processing.app.helpers.PreferencesMap; import processing.app.packages.UserLibraryFolder.Location; @@ -230,7 +230,7 @@ public String getMaintainer() { return maintainer; } - public List getRequires() { + public List getRequires() { return null; } From cd57b1f4d683db61c1857dd4362d36262845bacb Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Jan 2017 15:28:31 +0100 Subject: [PATCH 03/12] Added helper methods to compare versions --- .../src/cc/arduino/contributions/VersionHelper.java | 3 +++ .../contributions/libraries/ContributedLibrary.java | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/arduino-core/src/cc/arduino/contributions/VersionHelper.java b/arduino-core/src/cc/arduino/contributions/VersionHelper.java index caf98c8f120..bead8d46ebf 100644 --- a/arduino-core/src/cc/arduino/contributions/VersionHelper.java +++ b/arduino-core/src/cc/arduino/contributions/VersionHelper.java @@ -65,4 +65,7 @@ public static Optional valueOf(String ver) { } } + public static int compare(String a, String b) { + return valueOf(a).get().compareTo(valueOf(b).get()); + } } diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java index 485d6183bb3..6c42ddcfb37 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -32,12 +32,13 @@ import cc.arduino.contributions.DownloadableContribution; import processing.app.I18n; import processing.app.packages.UserLibrary; +import static processing.app.I18n.tr; import java.util.Comparator; import java.util.List; import java.util.Optional; -import static processing.app.I18n.tr; +import cc.arduino.contributions.VersionHelper; public abstract class ContributedLibrary extends DownloadableContribution { @@ -181,6 +182,10 @@ public boolean equals(Object obj) { return versionEquals && nameEquals; } + public boolean isBefore(ContributedLibrary other) { + return VersionHelper.compare(getVersion(), other.getVersion()) < 0; + } + @Override public int hashCode() { String hashingData = "CONTRIBUTEDLIB" + getName() + getVersion(); From 8ba835a02e918fe4d2bc901647f3683ef88fb516 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Jan 2017 15:44:50 +0100 Subject: [PATCH 04/12] Added library-dependency resolver --- .../libraries/LibrariesIndex.java | 71 +++++++++ .../UnavailableContributedLibrary.java | 142 ++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java index 7998525a152..8c15a95c904 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java @@ -29,6 +29,7 @@ package cc.arduino.contributions.libraries; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -98,4 +99,74 @@ public Optional getInstalled(String name) { ContributedLibraryReleases rel = new ContributedLibraryReleases(find(name)); return rel.getInstalled(); } + + public List resolveDependeciesOf(ContributedLibrary library) { + List solution = new ArrayList<>(); + solution.add(library); + if (resolveDependeciesOf(solution, library)) { + return solution; + } else { + return null; + } + } + + public boolean resolveDependeciesOf(List solution, + ContributedLibrary library) { + List requirements = library.getRequires(); + if (requirements == null) { + // No deps for this library, great! + return true; + } + + for (ContributedLibraryDependency dep : requirements) { + + // If the current solution already contains this dependency, skip over + boolean alreadyInSolution = false; + for (ContributedLibrary c : solution) { + if (c.getName().equals(dep.getName())) + alreadyInSolution = true; + } + if (alreadyInSolution) + continue; + + // Generate possible matching dependencies + List possibleDeps = findMatchingDependencies(dep); + + // If there are no dependencies available add as "missing" lib + if (possibleDeps.isEmpty()) { + solution.add(new UnavailableContributedLibrary(dep)); + continue; + } + + // Pick the latest version among possible deps + ContributedLibrary last = possibleDeps.stream() + .reduce((a, b) -> b.isBefore(a) ? a : b).get(); + + // Add dependecy to the solution and process recursively + solution.add(last); + if (!resolveDependeciesOf(solution, last)) { + return false; + } + } + return true; + } + + private List findMatchingDependencies(ContributedLibraryDependency dep) { + List available = find(dep.getName()); + if (dep.getVersionRequired() == null || dep.getVersionRequired().isEmpty()) + return available; + + // XXX: The following part is actually never reached. The use of version + // constraints requires a much complex backtracking algorithm, the following + // is just a draft placeholder. + +// List match = available.stream() +// // TODO: add more complex version comparators (> >= < <= ~ 1.0.* 1.*...) +// .filter(candidate -> candidate.getParsedVersion() +// .equals(dep.getVersionRequired())) +// .collect(Collectors.toList()); +// return match; + + return available; + } } diff --git a/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java new file mode 100644 index 00000000000..982aec81003 --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java @@ -0,0 +1,142 @@ +/* + * This file is part of Arduino. + * + * Copyright 2017 Arduino LLC (http://www.arduino.cc/) + * + * Arduino 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 2 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + */ + +package cc.arduino.contributions.libraries; + +import java.util.ArrayList; +import java.util.List; + +public class UnavailableContributedLibrary extends ContributedLibrary { + + private String name; + private String version; + + public UnavailableContributedLibrary(ContributedLibraryDependency dependency) { + this(dependency.getName(), dependency.getVersionRequired()); + } + + public UnavailableContributedLibrary(String _name, String _version) { + name = _name; + version = _version; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getMaintainer() { + return "Unknown"; + } + + @Override + public String getAuthor() { + return "Unknown"; + } + + @Override + public String getWebsite() { + return "Unknown"; + } + + @Override + public String getCategory() { + return "Uncategorized"; + } + + @Override + public void setCategory(String category) { + } + + @Override + public String getLicense() { + return "Unknown"; + } + + @Override + public String getParagraph() { + return ""; + } + + @Override + public String getSentence() { + return ""; + } + + @Override + public List getArchitectures() { + return new ArrayList<>(); + } + + @Override + public List getTypes() { + return new ArrayList<>(); + } + + @Override + public List getRequires() { + return new ArrayList<>(); + } + + @Override + public String getUrl() { + return ""; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public String getChecksum() { + return ""; + } + + @Override + public long getSize() { + return 0; + } + + @Override + public String getArchiveFileName() { + return ""; + } + + @Override + public String toString() { + return "!" + super.toString(); + } + + @Override + public List getProvidesIncludes() { + return new ArrayList<>(); + } +} From d7c1c872ef8bcda10ed07b9a6ce77e1789ceace1 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Jan 2017 17:35:10 +0100 Subject: [PATCH 05/12] Lib installer: factored out method to perform lib installation The new method will be used in next commits to handle installations of multiple libraries. This commit fix also minor bug in progress bar. --- .../libraries/LibraryInstaller.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java index 4b4fb7f7dbb..ef1729b6c6a 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java @@ -87,6 +87,16 @@ public synchronized void updateIndex(ProgressListener progressListener) throws E } public synchronized void install(ContributedLibrary lib, Optional mayReplacedLib, ProgressListener progressListener) throws Exception { + final MultiStepProgress progress = new MultiStepProgress(4); + + // Do install library (3 steps) + performInstall(lib, mayReplacedLib, progressListener, progress); + + // Rescan index (1 step) + rescanLibraryIndex(progress, progressListener); + } + + private void performInstall(ContributedLibrary lib, Optional mayReplacedLib, ProgressListener progressListener, MultiStepProgress progress) throws Exception { if (lib.isLibraryInstalled()) { System.out.println(I18n.format(tr("Library is already installed: {0}:{1}"), lib.getName(), lib.getParsedVersion())); return; @@ -94,8 +104,6 @@ public synchronized void install(ContributedLibrary lib, Optional Date: Thu, 26 Jan 2017 18:00:53 +0100 Subject: [PATCH 06/12] LibraryInstaller now autodetects if a library is being replaced It's no more required to pass this information from outside, just library that is being installed is now sufficient. --- .../libraries/ui/LibraryManagerUI.java | 6 ++--- app/src/processing/app/Base.java | 2 +- .../libraries/LibraryInstaller.java | 27 ++++++++++++++----- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java index 7ff1925878c..3847920b642 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java +++ b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java @@ -85,7 +85,7 @@ protected void onInstall(ContributedLibrary selectedLibrary, Optional mayReplaced) { + public void onInstallPressed(final ContributedLibrary lib) { clearErrorMessage(); installerThread = new Thread(() -> { try { setProgressVisible(true, tr("Installing...")); - installer.install(lib, mayReplaced, this::setProgress); + installer.install(lib, this::setProgress); // TODO: Do a better job in refreshing only the needed element if (contribTable.getCellEditor() != null) { contribTable.getCellEditor().stopCellEditing(); diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index ef4d9b30a4e..c8591cc2007 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -384,7 +384,7 @@ public Base(String[] args) throws Exception { library, mayInstalled.get().getParsedVersion()))); libraryInstaller.remove(mayInstalled.get(), progressListener); } else { - libraryInstaller.install(selected, mayInstalled, progressListener); + libraryInstaller.install(selected, progressListener); } } diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java index ef1729b6c6a..8d040effc74 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java @@ -43,6 +43,8 @@ import java.io.File; import java.io.IOException; import java.net.URL; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import static processing.app.I18n.tr; @@ -86,22 +88,35 @@ public synchronized void updateIndex(ProgressListener progressListener) throws E rescanLibraryIndex(progress, progressListener); } - public synchronized void install(ContributedLibrary lib, Optional mayReplacedLib, ProgressListener progressListener) throws Exception { + public synchronized void install(ContributedLibrary lib, ProgressListener progressListener) throws Exception { final MultiStepProgress progress = new MultiStepProgress(4); // Do install library (3 steps) - performInstall(lib, mayReplacedLib, progressListener, progress); + performInstall(lib, progressListener, progress); // Rescan index (1 step) rescanLibraryIndex(progress, progressListener); } - private void performInstall(ContributedLibrary lib, Optional mayReplacedLib, ProgressListener progressListener, MultiStepProgress progress) throws Exception { + private void performInstall(ContributedLibrary lib, ProgressListener progressListener, MultiStepProgress progress) throws Exception { if (lib.isLibraryInstalled()) { System.out.println(I18n.format(tr("Library is already installed: {0}:{1}"), lib.getName(), lib.getParsedVersion())); return; } + File libsFolder = BaseNoGui.getSketchbookLibrariesFolder().folder; + File destFolder = new File(libsFolder, lib.getName().replaceAll(" ", "_")); + + // Check if we are replacing an already installed lib + LibrariesIndex index = BaseNoGui.librariesIndexer.getIndex(); + Optional replacedLib = index.find(lib.getName()).stream() // + .filter(l -> l.getInstalledLibrary().isPresent()) // + .filter(l -> l.getInstalledLibrary().get().getInstalledFolder().equals(destFolder)) // + .findAny(); + if (!replacedLib.isPresent() && destFolder.exists()) { + System.out.println(I18n.format(tr("Library {0} is already installed in: {1}"), lib.getName(), destFolder)); + return; + } DownloadableContributionsDownloader downloader = new DownloadableContributionsDownloader(BaseNoGui.librariesIndexer.getStagingFolder()); // Step 1: Download library @@ -120,7 +135,6 @@ private void performInstall(ContributedLibrary lib, Optional // Step 2: Unpack library on the correct location progress.setStatus(I18n.format(tr("Installing library: {0}:{1}"), lib.getName(), lib.getParsedVersion())); progressListener.onProgress(progress); - File libsFolder = BaseNoGui.getSketchbookLibrariesFolder().folder; File tmpFolder = FileUtils.createTempFolder(libsFolder); try { new ArchiveExtractor(platform).extract(lib.getDownloadedFile(), tmpFolder, 1); @@ -132,10 +146,9 @@ private void performInstall(ContributedLibrary lib, Optional // Step 3: Remove replaced library and move installed one to the correct location // TODO: Fix progress bar... - if (mayReplacedLib.isPresent()) { - remove(mayReplacedLib.get(), progressListener); + if (replacedLib.isPresent()) { + remove(replacedLib.get(), progressListener); } - File destFolder = new File(libsFolder, lib.getName().replaceAll(" ", "_")); tmpFolder.renameTo(destFolder); progress.stepDone(); } From d4dff767a169903204101cba43a25502907ecc24 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Jan 2017 18:51:07 +0100 Subject: [PATCH 07/12] Now libraries are installed with all the dependencies This is the base for the GUI that will be introduced in the next commits. --- .../libraries/ui/LibraryManagerUI.java | 14 +++++++++++++- .../libraries/LibraryInstaller.java | 16 ++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java index 3847920b642..16f70f581ef 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java +++ b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java @@ -214,11 +214,23 @@ protected void onUpdatePressed() { } public void onInstallPressed(final ContributedLibrary lib) { + List deps = BaseNoGui.librariesIndexer.getIndex().resolveDependeciesOf(lib); + final boolean installDeps; + if (deps.size() > 1) { + System.out.println("The library requires dependencies!"); + installDeps = true; + } else { + installDeps = false; + } clearErrorMessage(); installerThread = new Thread(() -> { try { setProgressVisible(true, tr("Installing...")); - installer.install(lib, this::setProgress); + if (installDeps) { + installer.install(deps, this::setProgress); + } else { + installer.install(lib, this::setProgress); + } // TODO: Do a better job in refreshing only the needed element if (contribTable.getCellEditor() != null) { contribTable.getCellEditor().stopCellEditing(); diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java index 8d040effc74..de91c049090 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java @@ -88,11 +88,19 @@ public synchronized void updateIndex(ProgressListener progressListener) throws E rescanLibraryIndex(progress, progressListener); } - public synchronized void install(ContributedLibrary lib, ProgressListener progressListener) throws Exception { - final MultiStepProgress progress = new MultiStepProgress(4); + public void install(ContributedLibrary lib, ProgressListener progressListener) throws Exception { + ArrayList libs = new ArrayList<>(); + libs.add(lib); + install(libs, progressListener); + } + + public synchronized void install(List libs, ProgressListener progressListener) throws Exception { + MultiStepProgress progress = new MultiStepProgress(3 * libs.size() + 1); - // Do install library (3 steps) - performInstall(lib, progressListener, progress); + for (ContributedLibrary lib : libs) { + // Do install library (3 steps) + performInstall(lib, progressListener, progress); + } // Rescan index (1 step) rescanLibraryIndex(progress, progressListener); From d852146d6ba15fa06af40e6446c0bf66366e1c2e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 30 Jan 2017 17:26:56 +0100 Subject: [PATCH 08/12] Added library dependencies install dialog --- .../ui/ContributedLibraryTableCellJPanel.java | 1 + .../libraries/ui/LibraryManagerUI.java | 18 +- .../ui/MultiLibraryInstallDialog.java | 177 ++++++++++++++++++ 3 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 app/src/cc/arduino/contributions/libraries/ui/MultiLibraryInstallDialog.java diff --git a/app/src/cc/arduino/contributions/libraries/ui/ContributedLibraryTableCellJPanel.java b/app/src/cc/arduino/contributions/libraries/ui/ContributedLibraryTableCellJPanel.java index 99d4b66d23e..d4382cf5147 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/ContributedLibraryTableCellJPanel.java +++ b/app/src/cc/arduino/contributions/libraries/ui/ContributedLibraryTableCellJPanel.java @@ -213,6 +213,7 @@ public ContributedLibraryTableCellJPanel(JTable parentTable, Object value, } } + // TODO Make this a method of Theme private JTextPane makeNewDescription() { if (getComponentCount() > 0) { remove(0); diff --git a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java index 16f70f581ef..73385426ebe 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java +++ b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java @@ -52,6 +52,7 @@ import cc.arduino.contributions.libraries.ContributedLibraryReleases; import cc.arduino.contributions.libraries.LibraryInstaller; import cc.arduino.contributions.libraries.LibraryTypeComparator; +import cc.arduino.contributions.libraries.ui.MultiLibraryInstallDialog.Result; import cc.arduino.contributions.ui.DropdownItem; import cc.arduino.contributions.ui.FilteredAbstractTableModel; import cc.arduino.contributions.ui.InstallerJDialog; @@ -215,18 +216,23 @@ protected void onUpdatePressed() { public void onInstallPressed(final ContributedLibrary lib) { List deps = BaseNoGui.librariesIndexer.getIndex().resolveDependeciesOf(lib); - final boolean installDeps; - if (deps.size() > 1) { - System.out.println("The library requires dependencies!"); - installDeps = true; + boolean depsInstalled = deps.stream().allMatch(l -> l.getInstalledLibrary().isPresent() || l.getName().equals(lib.getName())); + Result installDeps; + if (!depsInstalled) { + MultiLibraryInstallDialog dialog; + dialog = new MultiLibraryInstallDialog(this, lib, deps); + dialog.setVisible(true); + installDeps = dialog.getInstallDepsResult(); + if (installDeps == Result.CANCEL) + return; } else { - installDeps = false; + installDeps = Result.NONE; } clearErrorMessage(); installerThread = new Thread(() -> { try { setProgressVisible(true, tr("Installing...")); - if (installDeps) { + if (installDeps == Result.ALL) { installer.install(deps, this::setProgress); } else { installer.install(lib, this::setProgress); diff --git a/app/src/cc/arduino/contributions/libraries/ui/MultiLibraryInstallDialog.java b/app/src/cc/arduino/contributions/libraries/ui/MultiLibraryInstallDialog.java new file mode 100644 index 00000000000..8996b57f6af --- /dev/null +++ b/app/src/cc/arduino/contributions/libraries/ui/MultiLibraryInstallDialog.java @@ -0,0 +1,177 @@ +/* + * This file is part of Arduino. + * + * Copyright 2017 Arduino LLC (http://www.arduino.cc/) + * + * Arduino 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 2 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + */ + +package cc.arduino.contributions.libraries.ui; + +import static processing.app.I18n.format; +import static processing.app.I18n.tr; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Insets; +import java.awt.Window; +import java.awt.event.WindowEvent; +import java.util.List; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JTextPane; +import javax.swing.WindowConstants; +import javax.swing.border.EmptyBorder; +import javax.swing.text.Document; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.StyleSheet; + +import cc.arduino.contributions.libraries.ContributedLibrary; +import cc.arduino.contributions.libraries.UnavailableContributedLibrary; +import processing.app.Base; +import processing.app.Theme; + +public class MultiLibraryInstallDialog extends JDialog { + + enum Result { + ALL, NONE, CANCEL + } + + Result result = Result.CANCEL; + + public MultiLibraryInstallDialog(Window parent, ContributedLibrary lib, + List dependencies) { + super(parent, format(tr("Dependencies for library {0}:{1}"), lib.getName(), + lib.getParsedVersion()), + ModalityType.APPLICATION_MODAL); + Container pane = getContentPane(); + pane.setLayout(new BorderLayout()); + + pane.add(Box.createHorizontalStrut(10), BorderLayout.WEST); + pane.add(Box.createHorizontalStrut(10), BorderLayout.EAST); + + { + JButton cancel = new JButton(tr("Cancel")); + cancel.addActionListener(ev -> { + result = Result.CANCEL; + setVisible(false); + }); + + JButton all = new JButton(tr("Install all")); + all.addActionListener(ev -> { + result = Result.ALL; + setVisible(false); + }); + + JButton none = new JButton(format(tr("Install '{0}' only"), lib.getName())); + none.addActionListener(ev -> { + result = Result.NONE; + setVisible(false); + }); + + Box buttonsBox = Box.createHorizontalBox(); + buttonsBox.add(all); + buttonsBox.add(Box.createHorizontalStrut(5)); + buttonsBox.add(none); + buttonsBox.add(Box.createHorizontalStrut(5)); + buttonsBox.add(cancel); + + JPanel buttonsPanel = new JPanel(); + buttonsPanel.setBorder(new EmptyBorder(7, 10, 7, 10)); + buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.Y_AXIS)); + buttonsPanel.add(buttonsBox); + + pane.add(buttonsPanel, BorderLayout.SOUTH); + } + + { + String libName = format("{0}:{1}", lib.getName(), + lib.getParsedVersion()); + String desc = format(tr("The library {0} needs some other library
dependencies currently not installed:"), + libName); + desc += "

"; + for (ContributedLibrary l : dependencies) { + if (l.getName().equals(lib.getName())) + continue; + if (l.getInstalledLibrary().isPresent()) + continue; + if (l instanceof UnavailableContributedLibrary) + continue; + desc += format("- {0}
", l.getName()); + } + desc += "
"; + desc += tr("Would you like to install also all the missing dependencies?"); + + JTextPane textArea = makeNewDescription(); + textArea.setContentType("text/html"); + textArea.setText(desc); + + JPanel libsList = new JPanel(); + libsList.setLayout(new BoxLayout(libsList, BoxLayout.Y_AXIS)); + libsList.add(textArea); + libsList.setBorder(new EmptyBorder(7, 7, 7, 7)); + pane.add(libsList, BorderLayout.NORTH); + } + + pack(); + setResizable(false); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + WindowEvent closing = new WindowEvent(this, WindowEvent.WINDOW_CLOSING); + Base.registerWindowCloseKeys(getRootPane(), e -> dispatchEvent(closing)); + } + + // TODO Make this a method of Theme + private JTextPane makeNewDescription() { + JTextPane description = new JTextPane(); + description.setInheritsPopupMenu(true); + Insets margin = description.getMargin(); + margin.bottom = 0; + description.setMargin(margin); + description.setContentType("text/html"); + Document doc = description.getDocument(); + if (doc instanceof HTMLDocument) { + HTMLDocument html = (HTMLDocument) doc; + StyleSheet s = html.getStyleSheet(); + s.addRule("body { margin: 0; padding: 0;" + + "font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;" + + "color: black;" + "font-size: " + 15 * Theme.getScale() / 100 + + "; }"); + } + description.setOpaque(false); + description.setBorder(new EmptyBorder(4, 7, 7, 7)); + description.setHighlighter(null); + description.setEditable(false); + add(description, 0); + return description; + } + + public Result getInstallDepsResult() { + return result; + } +} From 976ddb9e8b29548e0d892627daca69df88b1d42d Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 20 Feb 2017 13:41:25 +0100 Subject: [PATCH 09/12] When resolving dependencies consider installed contributions first Consider a case where the user decides to install a library `A` that depends on library `B` and `B` is not up-to-date (i.e. is installed a version that is not the latest), then the user is asked to "install" both libraries `A` and `B`, effectively upgrading `B`. With this change the already installed library `B` is left untouched and not displayed in the missing dependencies. --- .../libraries/LibrariesIndex.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java index 8c15a95c904..b42a6afb7f7 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java @@ -138,13 +138,20 @@ public boolean resolveDependeciesOf(List solution, continue; } - // Pick the latest version among possible deps - ContributedLibrary last = possibleDeps.stream() - .reduce((a, b) -> b.isBefore(a) ? a : b).get(); + // Pick the installed version if available + ContributedLibrary selected; + Optional installed = possibleDeps.stream() + .filter(l -> l.getInstalledLibrary().isPresent()).findAny(); + if (installed.isPresent()) { + selected = installed.get(); + } else { + // otherwise pick the latest version + selected = possibleDeps.stream().reduce((a, b) -> b.isBefore(a) ? a : b).get(); + } - // Add dependecy to the solution and process recursively - solution.add(last); - if (!resolveDependeciesOf(solution, last)) { + // Add dependency to the solution and process recursively + solution.add(selected); + if (!resolveDependeciesOf(solution, selected)) { return false; } } From f5f90db434b8a7f64aee2813f18799d7b2081388 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 13 Mar 2019 14:50:58 +0100 Subject: [PATCH 10/12] Better names for ContributedLibrary dependencies fields --- .../arduino/contributions/libraries/ContributedLibrary.java | 6 +++--- .../libraries/ContributedLibraryDependency.java | 4 ++-- .../cc/arduino/contributions/libraries/LibrariesIndex.java | 4 ++-- .../libraries/UnavailableContributedLibrary.java | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java index 6c42ddcfb37..692a3dfb20b 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -64,7 +64,7 @@ public abstract class ContributedLibrary extends DownloadableContribution { public abstract List getTypes(); - public abstract List getRequires(); + public abstract List getDependencies(); public abstract List getProvidesIncludes(); @@ -146,8 +146,8 @@ public String info() { } res += "\n"; res += " requires :\n"; - if (getRequires() != null) - for (ContributedLibraryDependency r : getRequires()) { + if (getDependencies() != null) + for (ContributedLibraryDependency r : getDependencies()) { res += " " + r; } res += "\n"; diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibraryDependency.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibraryDependency.java index 2683d5eb046..e8e500dd585 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibraryDependency.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibraryDependency.java @@ -33,10 +33,10 @@ public abstract class ContributedLibraryDependency { public abstract String getName(); - public abstract String getVersionRequired(); + public abstract String getVersion(); @Override public String toString() { - return getName() + " " + getVersionRequired(); + return getName() + " " + getVersion(); } } diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java index b42a6afb7f7..371d859515b 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java @@ -112,7 +112,7 @@ public List resolveDependeciesOf(ContributedLibrary library) public boolean resolveDependeciesOf(List solution, ContributedLibrary library) { - List requirements = library.getRequires(); + List requirements = library.getDependencies(); if (requirements == null) { // No deps for this library, great! return true; @@ -160,7 +160,7 @@ public boolean resolveDependeciesOf(List solution, private List findMatchingDependencies(ContributedLibraryDependency dep) { List available = find(dep.getName()); - if (dep.getVersionRequired() == null || dep.getVersionRequired().isEmpty()) + if (dep.getVersion() == null || dep.getVersion().isEmpty()) return available; // XXX: The following part is actually never reached. The use of version diff --git a/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java index 982aec81003..1e0468aa9df 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java @@ -38,7 +38,7 @@ public class UnavailableContributedLibrary extends ContributedLibrary { private String version; public UnavailableContributedLibrary(ContributedLibraryDependency dependency) { - this(dependency.getName(), dependency.getVersionRequired()); + this(dependency.getName(), dependency.getVersion()); } public UnavailableContributedLibrary(String _name, String _version) { @@ -101,7 +101,7 @@ public List getTypes() { } @Override - public List getRequires() { + public List getDependencies() { return new ArrayList<>(); } From 4268ce981650678d59c8564cc023468613931ac7 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 15 Jul 2019 22:56:08 +0200 Subject: [PATCH 11/12] Fix some linter suggestions --- .../libraries/ui/MultiLibraryInstallDialog.java | 2 +- .../contributions/libraries/LibrariesIndex.java | 11 +++++------ .../libraries/UnavailableContributedLibrary.java | 1 + 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/cc/arduino/contributions/libraries/ui/MultiLibraryInstallDialog.java b/app/src/cc/arduino/contributions/libraries/ui/MultiLibraryInstallDialog.java index 8996b57f6af..75f7703f430 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/MultiLibraryInstallDialog.java +++ b/app/src/cc/arduino/contributions/libraries/ui/MultiLibraryInstallDialog.java @@ -62,7 +62,7 @@ enum Result { ALL, NONE, CANCEL } - Result result = Result.CANCEL; + private Result result = Result.CANCEL; public MultiLibraryInstallDialog(Window parent, ContributedLibrary lib, List dependencies) { diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java index 371d859515b..d66a31fd3ce 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java @@ -38,6 +38,8 @@ import java.util.Optional; import java.util.stream.Collectors; +import cc.arduino.contributions.VersionComparator; + public abstract class LibrariesIndex { public abstract List getLibraries(); @@ -121,11 +123,8 @@ public boolean resolveDependeciesOf(List solution, for (ContributedLibraryDependency dep : requirements) { // If the current solution already contains this dependency, skip over - boolean alreadyInSolution = false; - for (ContributedLibrary c : solution) { - if (c.getName().equals(dep.getName())) - alreadyInSolution = true; - } + boolean alreadyInSolution = solution.stream() + .anyMatch(l -> l.getName().equals(dep.getName())); if (alreadyInSolution) continue; @@ -146,7 +145,7 @@ public boolean resolveDependeciesOf(List solution, selected = installed.get(); } else { // otherwise pick the latest version - selected = possibleDeps.stream().reduce((a, b) -> b.isBefore(a) ? a : b).get(); + selected = possibleDeps.stream().reduce(VersionComparator::max).get(); } // Add dependency to the solution and process recursively diff --git a/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java index 1e0468aa9df..277d969dede 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java @@ -73,6 +73,7 @@ public String getCategory() { @Override public void setCategory(String category) { + // Empty } @Override From f440fe357c6f1e8e75f3a61a2fe307c291858a0f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 15 Jul 2019 23:29:33 +0200 Subject: [PATCH 12/12] Make the dialog appear in the correct position --- .../cc/arduino/contributions/libraries/ui/LibraryManagerUI.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java index 73385426ebe..66ad0e44ba5 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java +++ b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java @@ -221,6 +221,7 @@ public void onInstallPressed(final ContributedLibrary lib) { if (!depsInstalled) { MultiLibraryInstallDialog dialog; dialog = new MultiLibraryInstallDialog(this, lib, deps); + dialog.setLocationRelativeTo(this); dialog.setVisible(true); installDeps = dialog.getInstallDepsResult(); if (installDeps == Result.CANCEL)