Java tutorial
package org.virtualAsylum.spriggan.data; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import javafx.beans.property.*; import javafx.collections.FXCollections; import javafx.event.ActionEvent; import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.internal.storage.file.FileRepository; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.transport.FetchResult; import org.virtualAsylum.spriggan.Database; import org.virtualAsylum.spriggan.Main; import org.virtualAsylum.spriggan.interfaces.popups.DependencyPopup; import org.virtualAsylum.spriggan.interfaces.MainInterface; import java.io.File; import java.io.FileFilter; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.*; import static com.sun.javafx.application.PlatformImpl.runLater; import static java.nio.file.Files.copy; import static org.virtualAsylum.spriggan.util.SprigganHelper.*; /** * Created by Morgan on 27/03/2014. */ public class Addon { Git git; Exception lastException; private void handleException(Exception ex) { Main.handleException(lastException = ex); } public void download(EventObject event) { download(); } public void download() { daemon(this::doDownload); } public State doDownload() { log("Downloading %s (%s)", getDisplayName(), getID()); setState(State.DOWNLOADING); State result = State.IDLE; setStateProgress(-1); Database.getCurrent().getRepository().add(this); File repositoryDirectory = getRepositoryDirectory(this); if (repositoryDirectory.exists()) { recursiveDelete(repositoryDirectory); } try { git = Git.cloneRepository().setURI(getRemoteRepository()).setBranch(getBranch()) .setDirectory(repositoryDirectory).setProgressMonitor(monitor).call(); Database.save(); } catch (Exception ex) { handleException(ex); result = State.ERROR; } log("Downloading %s (%s): %s", getDisplayName(), getID(), result); setState(result); setStateProgress(0); return result; } public void downloadAndInstall(EventObject event) { downloadAndInstall(); } public void downloadAndInstall() { daemon(this::doDownloadAndInstall); } public State doDownloadAndInstall() { if (doDownload().equals(State.IDLE)) { return doInstall(); } return State.ERROR; } public void install(EventObject event) { install(); } public void install() { daemon(this::doInstall); } static final List<String> DEFAULT_INSTALL_IGNORE = Arrays.asList(".git", ".gitignore", ".spriggan", "README.md", "LICENSE.md", "readme", "readme.md", "license", "license.md", "LICENSE", "README"); private ArrayList<File> getRepositoryFiles() { ArrayList<File> result = new ArrayList(); File repositoryDirectory = getRepositoryDirectory(this); Collection<String> ignore = getInstallIgnore(); for (File file : repositoryDirectory.listFiles()) { getRepositoryFiles_Sub(file, repositoryDirectory, result, ignore); } return result; } private void getRepositoryFiles_Sub(File file, File repositoryDirectory, ArrayList<File> result, Collection<String> ignore) { Path path = repositoryDirectory.toPath().relativize(file.toPath()); if (ignore.contains(path.toString()) || DEFAULT_INSTALL_IGNORE.contains(path.toString())) { return; } result.add(file); if (file.isDirectory()) { for (File sub : file.listFiles()) { getRepositoryFiles_Sub(sub, repositoryDirectory, result, ignore); } } } public State doInstall() { log("Installing %s (%s)", getDisplayName(), getID()); State result = State.IDLE; State error = State.ERROR; setState(State.INSTALLING); setStateProgress(-1); boolean didError = false; try { //<editor-fold desc="Dependencies"> Collection<String> dependencyIDs = getDependencies(); ArrayList<Addon> dependencies = new ArrayList(); if (dependencyIDs.size() > 0) { setState(State.INSTALLING_DEPENDENCIES); for (String dependencyIDString : dependencyIDs) { Addon dependency = Database.find_ID(dependencyIDString); if (dependency == null) { throw new Exception(String.format("Dependency %s was missing for %s(%s)", dependencyIDString, getDisplayName(), getID())); } if (!dependency.getInstalled()) { dependencies.add(dependency); } } if (dependencies.size() > 0) { SimpleObjectProperty<Boolean> popupResult = new SimpleObjectProperty(null); runLater(() -> { DependencyPopup popup = new DependencyPopup(this, dependencies, popupResult); Stage stage = popup.popup(StageStyle.UTILITY, Modality.WINDOW_MODAL, MainInterface.current.getWindow(), getDisplayName()); stage.setOnCloseRequest(e -> popupResult.set(false)); stage.show(); }); while (popupResult.get() == null) { sleep(500); } if (!popupResult.get()) { error = State.IDLE; throw new Exception(String.format("Did not install dependencies for %s (%s)", getDisplayName(), getID())); } double perDep = 1.0 / dependencies.size(); setStateProgress(0.0); for (Addon dependency : dependencies) { boolean downloaded = Database.getCurrent().getRepository().contains(dependency); State depResult = State.IDLE; if (!downloaded) { depResult = dependency.doDownloadAndInstall(); } else if (!dependency.getInstalled()) { depResult = dependency.doInstall(); } if (depResult != State.IDLE) { throw new Exception( String.format("Dependency %s (%s) failed to download and/or install", dependency.getDisplayName(), dependency.getID())); } incrementStateProgress(perDep); } setState(State.INSTALLING); } } //</editor-fold> error = State.ERROR; final File repositoryDirectory = getRepositoryDirectory(this); final File installDirectory = getInstallDirectory(this); ArrayList<File> files = getRepositoryFiles(); double perFile = 1.0 / files.size(); setStateProgress(0.0); for (File inputFile : files) { File outputFile = new File(installDirectory, repositoryDirectory.toPath().relativize(inputFile.toPath()).toString()); outputFile.getParentFile().mkdirs(); Files.copy(inputFile.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING); incrementStateProgress(perFile); } } catch (Exception ex) { if (error == State.ERROR) { handleException(ex); } result = error; didError = true; } if (!didError) { setInstalled(true); } log("Installing %s (%s): %s", getDisplayName(), getID(), result); setStateProgress(0); setState(result); return result; } public void uninstall(EventObject event) { uninstall(); } public void uninstall() { daemon(this::doUninstall); } public State doUninstall() { log("Uninstalling %s (%s)", getDisplayName(), getID()); State result = State.IDLE; setState(State.UNINSTALLING); setStateProgress(-1); final File repositoryDirectory = getRepositoryDirectory(this); final File installDirectory = getInstallDirectory(this); ArrayList<File> files = getRepositoryFiles(); double perFile = 1.0 / files.size(); setStateProgress(0.0); for (File inputFile : files) { File outputFile = new File(installDirectory, repositoryDirectory.toPath().relativize(inputFile.toPath()).toString()); if (outputFile.exists()) { outputFile.delete(); if (outputFile.getParentFile().listFiles().length == 0) { outputFile.getParentFile().delete(); } incrementStateProgress(perFile); } } if (result == State.IDLE) { setInstalled(false); } log("Uninstalling %s (%s): %s", getDisplayName(), getID(), result); setStateProgress(0); setState(result); return result; } private static void changeListener(Object a, Object b, Object c) { MainInterface.current.updateFilters(); } public static File getRepositoryDirectory(Addon addon) { return new File(Database.PATH_REPOSITORY, addon.getInstallName() + "/" + addon.getBranch()); } public boolean hasTag(String tag) { return tags.get().contains(tag); } public void update(ActionEvent e) { update(); } public void update() { daemon(this::doUpdate); } static Git getGit(Addon addon) { try { FileRepository repository = new FileRepository(new File(getRepositoryDirectory(addon), ".git")); return new Git(repository); } catch (Exception ex) { addon.handleException(ex); return null; } } public State doUpdate() { State result = State.IDLE; setStateProgress(-1); setState(State.UPDATING); try { FetchResult fetchResult = getGit(this).fetch().setDryRun(true).setProgressMonitor(monitor).call(); if (fetchResult.getTrackingRefUpdates().isEmpty()) { setState(State.IDLE); } else { setState(State.UPDATING); getGit(this).pull().setProgressMonitor(monitor).call(); if (getInstalled()) { doInstall(); } } } catch (Exception e) { e.printStackTrace(); } setState(result); setStateProgress(0.0); return result; } public static enum State { IDLE("Idle"), DOWNLOADING("Downloading"), ERROR("Error"), INSTALLING("Installing"), UNINSTALLING( "Uninstalling"), INSTALLING_DEPENDENCIES("Installing Dependencies"), UPDATING("Updating"); final String text; State(String text) { this.text = text; } @Override public String toString() { return text; } } private static class AddonProgressMonitor implements ProgressMonitor { final Addon addon; public AddonProgressMonitor(Addon addon) { this.addon = addon; } double done = 0, perItem; String title; @Override public void start(int totalTasks) { //System.out.printf("START: %s tasks\n", totalTasks); } @Override public void beginTask(String title, int totalWork) { addon.setStateProgress(done = 0); perItem = 1.0 / (totalWork + 1); //System.out.printf("BEGIN: %s, %s\n", this.title = title, totalWork); } @Override public void update(int completed) { addon.setStateProgress(done += perItem); //System.out.printf("UPDATE: %s, %s (%s)\n", title, completed, done); } @Override public void endTask() { addon.setStateProgress(1); //System.out.printf("END: %s\n", title); } @Override public boolean isCancelled() { return false; } } @Override public boolean equals(Object obj) { if (obj instanceof Addon) { Addon objA = (Addon) obj; return objA.getID().equals(getID()); } return false; } public static File getInstallDirectory(Addon addon) { return new File(Database.PATH_INSTALL, addon.getInstallName()); } SimpleObjectProperty<UUID> id = new SimpleObjectProperty(); @JsonProperty("id") public String getID() { return id.get().toString(); } @JsonIgnore public SimpleObjectProperty<UUID> idProperty() { return id; } @JsonProperty("id") public void setID(String id) { this.id.set(UUID.fromString(id)); } //<editor-fold desc="Dependencies"> SimpleListProperty<String> dependencies = new SimpleListProperty(FXCollections.observableArrayList()); public Collection<String> getDependencies() { return dependencies.get(); } @JsonIgnore public SimpleListProperty<String> dependenciesProperty() { return dependencies; } public void setDependencies(String[] dependencies) { if (dependencies == null || dependencies.length == 0) { return; } this.dependencies.set(FXCollections.observableArrayList(dependencies)); } //</editor-fold> //<editor-fold desc="Installed"> SimpleBooleanProperty installed = new SimpleBooleanProperty(); public boolean getInstalled() { return installed.get(); } @JsonIgnore public SimpleBooleanProperty installedProperty() { return installed; } public void setInstalled(boolean installed) { runLater(() -> { this.installed.set(installed); Database.save(); }); } //</editor-fold> //<editor-fold desc="State"> SimpleObjectProperty<State> state = new SimpleObjectProperty(State.IDLE); @JsonIgnore public State getState() { return state.get(); } @JsonIgnore public SimpleObjectProperty<State> stateProperty() { return state; } @JsonIgnore public void setState(State state) { runLater(() -> this.state.set(state)); } //</editor-fold> //<editor-fold desc="StateProgress"> SimpleDoubleProperty stateProgress = new SimpleDoubleProperty(0); @JsonIgnore public SimpleDoubleProperty stateProgressProperty() { return stateProgress; } @JsonIgnore public void setStateProgress(double stateProgress) { runLater(() -> this.stateProgress.set(stateProgress)); } public void incrementStateProgress(double value) { setStateProgress(stateProgress.get() + value); } //</editor-fold> //<editor-fold desc="RemoteRepository"> SimpleStringProperty remoteRepository = new SimpleStringProperty(); public String getRemoteRepository() { return remoteRepository.get(); } @JsonIgnore public SimpleStringProperty remoteRepositoryProperty() { return remoteRepository; } public void setRemoteRepository(String remoteRepository) { this.remoteRepository.set(remoteRepository); } //</editor-fold> @JsonAnySetter public void metaSet(String name, Object value) { } //<editor-fold desc="Readme"> SimpleObjectProperty<URL> readme = new SimpleObjectProperty(); public URL getReadme() { return readme.get(); } @JsonIgnore public SimpleObjectProperty<URL> readmeProperty() { return readme; } public void setReadme(URL root) { this.readme.set(root); } //</editor-fold> //<editor-fold desc="DisplayName"> SimpleStringProperty displayName = new SimpleStringProperty(); public String getDisplayName() { return displayName.get(); } @JsonIgnore public SimpleStringProperty displayNameProperty() { return displayName; } public void setDisplayName(String displayName) { this.displayName.set(displayName); } //</editor-fold> //<editor-fold desc="Author"> SimpleStringProperty author = new SimpleStringProperty(); public String getAuthor() { return author.get(); } @JsonIgnore public SimpleStringProperty authorProperty() { return author; } public void setAuthor(String author) { this.author.set(author); } //</editor-fold> //<editor-fold desc="WikiRoot"> SimpleStringProperty wikiRoot = new SimpleStringProperty(); public String getWikiRoot() { return wikiRoot.get(); } @JsonIgnore public SimpleStringProperty wikiRootProperty() { return wikiRoot; } public void setWikiRoot(String wikiRoot) { this.wikiRoot.set(wikiRoot); } //</editor-fold> //<editor-fold desc="InstallName"> SimpleStringProperty installName = new SimpleStringProperty(); public String getInstallName() { return installName.get(); } @JsonIgnore public SimpleStringProperty installNameProperty() { return installName; } public void setInstallName(String installName) { this.installName.set(installName); } //</editor-fold> //<editor-fold desc="Tags"> SimpleListProperty<String> tags = new SimpleListProperty(FXCollections.observableArrayList()); public Collection<String> getTags() { return tags.get(); } @JsonIgnore public SimpleListProperty<String> tagsProperty() { return tags; } public void setTags(String[] tags) { this.tags.set(FXCollections.observableArrayList(tags)); } //</editor-fold> //<editor-fold desc="InstallIgnore"> SimpleListProperty<String> installIgnore = new SimpleListProperty(FXCollections.observableArrayList()); public Collection<String> getInstallIgnore() { return installIgnore.get(); } @JsonIgnore public SimpleListProperty<String> installIgnoreProperty() { return installIgnore; } public void setInstallIgnore(String[] installIgnore) { if (installIgnore == null || installIgnore.length == 0) { return; } this.installIgnore.set(FXCollections.observableArrayList(installIgnore)); } //</editor-fold> //<editor-fold desc="Branch"> SimpleStringProperty branch = new SimpleStringProperty("master"); public String getBranch() { return branch.get(); } @JsonIgnore public SimpleStringProperty branchProperty() { return branch; } public void setBranch(String branch) { this.branch.set(branch); } //</editor-fold> private final AddonProgressMonitor monitor; public Addon() { monitor = new AddonProgressMonitor(this); state.addListener(Addon::changeListener); } }