From 010c2dad792a096e7b98f63578cc1b2bf1175b91 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 30 Jan 2017 17:24:30 +0100 Subject: [PATCH 01/11] Added collector to LibraryList --- .../processing/app/packages/LibraryList.java | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/arduino-core/src/processing/app/packages/LibraryList.java b/arduino-core/src/processing/app/packages/LibraryList.java index b83e5265ef2..4f7db2868a4 100644 --- a/arduino-core/src/processing/app/packages/LibraryList.java +++ b/arduino-core/src/processing/app/packages/LibraryList.java @@ -30,8 +30,15 @@ import java.io.File; import java.util.Collections; +import java.util.EnumSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; import processing.app.helpers.FileUtils; @@ -97,5 +104,36 @@ public synchronized boolean hasLibrary(UserLibrary lib) { if (l == lib) return true; return false; } -} + public static Collector collector() { + return new Collector() { + @Override + public Supplier supplier() { + return () -> new LibraryList(); + } + + @Override + public BiConsumer accumulator() { + return (libs, lib) -> libs.add(lib); + } + + @Override + public BinaryOperator combiner() { + return (we, they) -> { + we.addAll(they); + return we; + }; + } + + @Override + public Function finisher() { + return (libs) -> libs; + } + + @Override + public Set characteristics() { + return EnumSet.noneOf(Characteristics.class); + } + }; + } +} From a6fb0b2370af0ef664ba7266af7f5a2964e98d92 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 25 Jan 2017 15:34:14 +0100 Subject: [PATCH 02/11] 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 cb0d34c916b..72d742a7499 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -137,7 +137,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 @@ -147,9 +147,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 3a9cc94c285e3452bd1b29bcd66f553e452572e6 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 25 Jan 2017 17:21:50 +0100 Subject: [PATCH 03/11] 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 72d742a7499..ceb69b575a1 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -61,7 +61,7 @@ public abstract class ContributedLibrary extends DownloadableContribution { public abstract List getTypes(); - public abstract List getRequires(); + public abstract List getRequires(); public static final Comparator CASE_INSENSITIVE_ORDER = (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName()); @@ -117,7 +117,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 69ba15a3c34..4ce9a700a72 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -30,7 +30,7 @@ import cc.arduino.Constants; import cc.arduino.contributions.libraries.ContributedLibrary; -import cc.arduino.contributions.libraries.ContributedLibraryReference; +import cc.arduino.contributions.libraries.ContributedLibraryDependency; import processing.app.helpers.PreferencesMap; import java.io.File; @@ -244,7 +244,7 @@ public String getArchiveFileName() { } @Override - public List getRequires() { + public List getRequires() { return null; } From 936108782deedb4686cd8abe7401a4ad356030d3 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Jan 2017 15:28:31 +0100 Subject: [PATCH 04/11] Added helper methods to compare versions --- .../src/cc/arduino/contributions/VersionHelper.java | 3 +++ .../contributions/libraries/ContributedLibrary.java | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/arduino-core/src/cc/arduino/contributions/VersionHelper.java b/arduino-core/src/cc/arduino/contributions/VersionHelper.java index 0f108aed5a7..2fb4296a52e 100644 --- a/arduino-core/src/cc/arduino/contributions/VersionHelper.java +++ b/arduino-core/src/cc/arduino/contributions/VersionHelper.java @@ -64,4 +64,7 @@ public static Version valueOf(String ver) { } } + public static int compare(String a, String b) { + return valueOf(a).compareTo(valueOf(b)); + } } diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java index ceb69b575a1..eb5986d0e63 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -29,13 +29,14 @@ package cc.arduino.contributions.libraries; -import cc.arduino.contributions.DownloadableContribution; -import processing.app.I18n; +import static processing.app.I18n.tr; import java.util.Comparator; import java.util.List; -import static processing.app.I18n.tr; +import cc.arduino.contributions.DownloadableContribution; +import cc.arduino.contributions.VersionHelper; +import processing.app.I18n; public abstract class ContributedLibrary extends DownloadableContribution { @@ -152,6 +153,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 1dde868e77a8fa620b533897fbf0a2c1c4448e93 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Jan 2017 15:44:50 +0100 Subject: [PATCH 05/11] Added library-dependency resolver --- .../libraries/LibrariesIndex.java | 81 ++++++++++- .../UnavailableContributedLibrary.java | 137 ++++++++++++++++++ 2 files changed, 215 insertions(+), 3 deletions(-) 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 a78b6b63733..0e7918c6c95 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java @@ -29,13 +29,18 @@ package cc.arduino.contributions.libraries; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomComparator; import cc.arduino.contributions.filters.InstalledPredicate; import cc.arduino.contributions.libraries.filters.LibraryWithNamePredicate; -import java.util.*; -import java.util.stream.Collectors; - public abstract class LibrariesIndex { public abstract List getLibraries(); @@ -101,4 +106,74 @@ public ContributedLibrary getInstalled(String name) { return installedReleases.get(0); } + + 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..2da1114af16 --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/libraries/UnavailableContributedLibrary.java @@ -0,0 +1,137 @@ +/* + * 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(); + } +} From 471aff5c6c0df52a463f547cef4567f6acfdcbe7 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Jan 2017 17:35:10 +0100 Subject: [PATCH 06/11] 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 12ac6519ae4..36eb3533d85 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java @@ -83,6 +83,16 @@ public synchronized void updateIndex(ProgressListener progressListener) throws E } public synchronized void install(ContributedLibrary lib, ContributedLibrary replacedLib, ProgressListener progressListener) throws Exception { + final MultiStepProgress progress = new MultiStepProgress(4); + + // Do install library (3 steps) + performInstall(lib, replacedLib, progressListener, progress); + + // Rescan index (1 step) + rescanLibraryIndex(progress, progressListener); + } + + private void performInstall(ContributedLibrary lib, ContributedLibrary replacedLib, ProgressListener progressListener, MultiStepProgress progress) throws Exception { if (lib.isInstalled()) { System.out.println(I18n.format(tr("Library is already installed: {0} version {1}"), lib.getName(), lib.getParsedVersion())); return; @@ -90,8 +100,6 @@ public synchronized void install(ContributedLibrary lib, ContributedLibrary repl DownloadableContributionsDownloader downloader = new DownloadableContributionsDownloader(BaseNoGui.librariesIndexer.getStagingFolder()); - final MultiStepProgress progress = new MultiStepProgress(3); - // Step 1: Download library try { downloader.download(lib, progress, I18n.format(tr("Downloading library: {0}"), lib.getName()), progressListener); @@ -99,6 +107,7 @@ public synchronized void install(ContributedLibrary lib, ContributedLibrary repl // Download interrupted... just exit return; } + progress.stepDone(); // TODO: Extract to temporary folders and move to the final destination only // once everything is successfully unpacked. If the operation fails remove @@ -123,9 +132,6 @@ public synchronized void install(ContributedLibrary lib, ContributedLibrary repl File destFolder = new File(libsFolder, lib.getName().replaceAll(" ", "_")); tmpFolder.renameTo(destFolder); progress.stepDone(); - - // Step 4: Rescan index - rescanLibraryIndex(progress, progressListener); } public synchronized void remove(ContributedLibrary lib, ProgressListener progressListener) throws IOException { From 11baf2341d63301b8a3bb9405fee2e4e7e82eaaa Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Jan 2017 18:00:53 +0100 Subject: [PATCH 07/11] 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 | 21 ++++++++++++++----- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java index 4c94700197f..7906c037cc8 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java +++ b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java @@ -84,7 +84,7 @@ protected void onInstall(ContributedLibrary selectedLibrary, ContributedLibrary if (selectedLibrary.isReadOnly()) { onRemovePressed(installedLibrary); } else { - onInstallPressed(selectedLibrary, installedLibrary); + onInstallPressed(selectedLibrary); } } @@ -219,12 +219,12 @@ protected void onUpdatePressed() { installerThread.start(); } - public void onInstallPressed(final ContributedLibrary lib, final ContributedLibrary replaced) { + public void onInstallPressed(final ContributedLibrary lib) { clearErrorMessage(); installerThread = new Thread(() -> { try { setProgressVisible(true, tr("Installing...")); - installer.install(lib, replaced, this::setProgress); + installer.install(lib, this::setProgress); onIndexesUpdated(); // TODO: Do a better job in refreshing only the needed element //getContribModel().updateLibrary(lib); } catch (Exception e) { diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index f983ffe8f68..440744ede02 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -352,7 +352,7 @@ public Base(String[] args) throws Exception { if (selected.isReadOnly()) { libraryInstaller.remove(installed, progressListener); } else { - libraryInstaller.install(selected, installed, 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 36eb3533d85..423b7a1e976 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java @@ -82,22 +82,35 @@ public synchronized void updateIndex(ProgressListener progressListener) throws E rescanLibraryIndex(progress, progressListener); } - public synchronized void install(ContributedLibrary lib, ContributedLibrary replacedLib, 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, replacedLib, progressListener, progress); + performInstall(lib, progressListener, progress); // Rescan index (1 step) rescanLibraryIndex(progress, progressListener); } - private void performInstall(ContributedLibrary lib, ContributedLibrary replacedLib, ProgressListener progressListener, MultiStepProgress progress) throws Exception { + private void performInstall(ContributedLibrary lib, ProgressListener progressListener, MultiStepProgress progress) throws Exception { if (lib.isInstalled()) { System.out.println(I18n.format(tr("Library is already installed: {0} version {1}"), lib.getName(), lib.getParsedVersion())); return; } + File libsFolder = BaseNoGui.librariesIndexer.getSketchbookLibrariesFolder(); + File destFolder = new File(libsFolder, lib.getName().replaceAll(" ", "_")); + + // Check if we are replacing an already installed lib + ContributedLibrary replacedLib = null; + LibrariesIndex index = BaseNoGui.librariesIndexer.getIndex(); + for (ContributedLibrary l : index.find(lib.getName())) { + if (l.isInstalled() && l.getInstalledFolder().equals(destFolder)) { + replacedLib = l; + break; + } + } + DownloadableContributionsDownloader downloader = new DownloadableContributionsDownloader(BaseNoGui.librariesIndexer.getStagingFolder()); // Step 1: Download library @@ -116,7 +129,6 @@ private void performInstall(ContributedLibrary lib, ContributedLibrary replacedL // Step 2: Unpack library on the correct location progress.setStatus(I18n.format(tr("Installing library: {0}"), lib.getName())); progressListener.onProgress(progress); - File libsFolder = BaseNoGui.librariesIndexer.getSketchbookLibrariesFolder(); File tmpFolder = FileUtils.createTempFolder(libsFolder); try { new ArchiveExtractor(platform).extract(lib.getDownloadedFile(), tmpFolder, 1); @@ -129,7 +141,6 @@ private void performInstall(ContributedLibrary lib, ContributedLibrary replacedL // Step 3: Remove replaced library and move installed one to the correct location // TODO: Fix progress bar... remove(replacedLib, progressListener); - File destFolder = new File(libsFolder, lib.getName().replaceAll(" ", "_")); tmpFolder.renameTo(destFolder); progress.stepDone(); } From bae6bd77a8fb341fd68dbac466dab363997f5696 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Jan 2017 18:51:07 +0100 Subject: [PATCH 08/11] 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 | 15 ++++++++++++++- .../libraries/LibraryInstaller.java | 18 ++++++++++++++---- 2 files changed, 28 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 7906c037cc8..cf847af011f 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java +++ b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java @@ -38,6 +38,7 @@ import java.util.Collection; import java.util.Collections; import java.util.LinkedList; +import java.util.List; import java.util.function.Predicate; import javax.swing.Box; @@ -220,11 +221,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); + } onIndexesUpdated(); // TODO: Do a better job in refreshing only the needed element //getContribModel().updateLibrary(lib); } catch (Exception e) { diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java index 423b7a1e976..76cc35eb651 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 static processing.app.I18n.tr; @@ -82,11 +84,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 fb00f0297136ae745d5a1a987b7501312284e4cd Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 30 Jan 2017 17:26:56 +0100 Subject: [PATCH 09/11] 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 5a7c091683a..e4ba204db08 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/ContributedLibraryTableCellJPanel.java +++ b/app/src/cc/arduino/contributions/libraries/ui/ContributedLibraryTableCellJPanel.java @@ -205,6 +205,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 cf847af011f..252499eafb8 100644 --- a/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java +++ b/app/src/cc/arduino/contributions/libraries/ui/LibraryManagerUI.java @@ -51,6 +51,7 @@ import cc.arduino.contributions.libraries.ContributedLibrary; 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.DropdownAllItem; import cc.arduino.contributions.ui.DropdownItem; import cc.arduino.contributions.ui.FilteredAbstractTableModel; @@ -222,18 +223,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.isInstalled() || 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..24c03e37e9b --- /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.isInstalled()) + 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 abe867f4e578b35d5b2a53be7c244482b6dace0f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 20 Feb 2017 13:41:25 +0100 Subject: [PATCH 10/11] 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 | 20 +++++++++++++------ 1 file changed, 14 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 0e7918c6c95..7f37f095f0a 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndex.java @@ -35,6 +35,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomComparator; @@ -145,13 +146,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.isInstalled()).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 20ba6ff4e2beed144893d1282cdc262114b31c47 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 30 Jan 2017 17:26:07 +0100 Subject: [PATCH 11/11] TO BE REMOVED: helps in debugging --- .../arduino/contributions/libraries/LibrariesIndexer.java | 7 ++++++- .../arduino/contributions/libraries/LibraryInstaller.java | 1 + arduino-core/src/processing/app/BaseNoGui.java | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndexer.java b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndexer.java index a09eb2a468f..140f927fd9f 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndexer.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndexer.java @@ -68,7 +68,7 @@ public class LibrariesIndexer { private final List badLibNotified = new ArrayList<>(); public LibrariesIndexer(File preferencesFolder) { - indexFile = new File(preferencesFolder, "library_index.json"); + indexFile = new File(preferencesFolder, "library_index_test.json"); stagingFolder = new File(new File(preferencesFolder, "staging"), "libraries"); } @@ -99,6 +99,11 @@ private void parseIndex(File file) throws IOException { } finally { IOUtils.closeQuietly(indexIn); } + +// ContributedLibrary lib = index.find("ArduinoCloud","1.0.0"); +// System.out.println(lib.info()); +// System.out.println(index.resolveDependeciesOf(lib)); +// System.exit(0); } public void setLibrariesFolders(List _librariesFolders) { diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java index 76cc35eb651..8bb15a6ae75 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java @@ -57,6 +57,7 @@ public LibraryInstaller(Platform platform) { } public synchronized void updateIndex(ProgressListener progressListener) throws Exception { + if (true) return; final MultiStepProgress progress = new MultiStepProgress(2); DownloadableContributionsDownloader downloader = new DownloadableContributionsDownloader(BaseNoGui.librariesIndexer.getStagingFolder()); diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java index fd372596c5b..b8f719cdae7 100644 --- a/arduino-core/src/processing/app/BaseNoGui.java +++ b/arduino-core/src/processing/app/BaseNoGui.java @@ -487,6 +487,7 @@ static public void initPackages() throws Exception { try { librariesIndexer.parseIndex(); } catch (JsonProcessingException e) { + e.printStackTrace(); File librariesIndexFile = librariesIndexer.getIndexFile(); FileUtils.deleteIfExists(librariesIndexFile); }