Java tutorial
/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tools.idea.welcome.install; import com.google.common.base.Function; import com.google.common.base.Throwables; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.ThrowableComputable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.concurrent.atomic.AtomicBoolean; /** * <p>Operation that is executed when installing the operation.</p> * <p/> * <p>Type argument specifies the type of the operation return value and the argument</p> */ public abstract class InstallOperation<Return, Argument> { protected final InstallContext myContext; private final double myProgressRatio; protected InstallOperation(InstallContext context, double progressRatio) { myContext = context; myProgressRatio = progressRatio; } public static <Return, Argument> InstallOperation<Return, Argument> wrap(@NotNull InstallContext context, @NotNull Function<Argument, Return> function, double progressShare) { return new FunctionWrapper<Return, Argument>(context, function, progressShare); } /** * Performs the actual logic */ @NotNull protected abstract Return perform(@NotNull ProgressIndicator indicator, @NotNull Argument argument) throws WizardException, InstallationCancelledException; /** * Runs the operation under progress indicator that only gives access to progress portion. */ @NotNull public final Return execute(@NotNull final Argument argument) throws WizardException, InstallationCancelledException { myContext.checkCanceled(); if (myProgressRatio == 0) { return perform(new EmptyProgressIndicator(), argument); } else { try { return myContext.run(new ThrowableComputable<Return, Exception>() { @Override @Nullable public Return compute() throws Exception { ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator == null) { indicator = new EmptyProgressIndicator(); } return perform(indicator, argument); } }, myProgressRatio); } catch (ProcessCanceledException e) { throw new InstallationCancelledException(); } catch (Exception e) { Throwables.propagateIfPossible(e, WizardException.class, InstallationCancelledException.class); throw Throwables.propagate(e); } } } /** * Shows a retry prompt. Throws an exception to stop the setup process if the user preses cancel or returns normally otherwise. */ protected final void promptToRetry(@NotNull final String prompt, @NotNull String failureDescription, @Nullable Exception e) throws WizardException { final AtomicBoolean response = new AtomicBoolean(false); Application application = ApplicationManager.getApplication(); application.invokeAndWait(new Runnable() { @Override public void run() { int i = Messages.showYesNoDialog(null, prompt, "Android Studio Setup", "Retry", "Cancel", Messages.getErrorIcon()); response.set(i == Messages.YES); } }, application.getAnyModalityState()); if (!response.get()) { Throwables.propagateIfInstanceOf(e, WizardException.class); throw new WizardException(failureDescription, e); } else { myContext.print(failureDescription + "\n", ConsoleViewContentType.ERROR_OUTPUT); } } public abstract void cleanup(@NotNull Return result); /** * This allows combining a sequence of operations into one, assisting with the cleanup. */ public final <FinalResult> InstallOperation<FinalResult, Argument> then( @NotNull final InstallOperation<FinalResult, Return> next) { return new OperationChain<FinalResult, Argument, Return>(this, next); } /** * <p>Adds a function to a sequence, wrapping it into InstallOperation.</p> * * <p>Note that currently it is expected that the function is fast and there is no progress to report. * Another option is to manage progress manually.</p> */ public final <FinalResult> InstallOperation<FinalResult, Argument> then( @NotNull Function<Return, FinalResult> next) { return then(wrap(myContext, next, 0)); } private static class OperationChain<FinalResult, Argument, Return> extends InstallOperation<FinalResult, Argument> { private final InstallOperation<Return, Argument> myFirst; private final InstallOperation<FinalResult, Return> mySecond; public OperationChain(InstallOperation<Return, Argument> first, InstallOperation<FinalResult, Return> second) { super(first.myContext, 0); myFirst = first; mySecond = second; } @Override @NotNull protected FinalResult perform(@NotNull ProgressIndicator indicator, @NotNull Argument argument) throws WizardException, InstallationCancelledException { Return execute = myFirst.execute(argument); try { return mySecond.execute(execute); } finally { myFirst.cleanup(execute); } } @Override public void cleanup(@NotNull FinalResult result) { mySecond.cleanup(result); } } private static class FunctionWrapper<Return, Argument> extends InstallOperation<Return, Argument> { @NotNull private final Function<Argument, Return> myRunnable; public FunctionWrapper(@NotNull InstallContext context, @NotNull Function<Argument, Return> runnable, double progressShare) { super(context, progressShare); myRunnable = runnable; } @NotNull @Override protected Return perform(@NotNull ProgressIndicator indicator, @NotNull Argument arg) throws WizardException { indicator.start(); try { Return value = myRunnable.apply(arg); assert value != null; return value; } finally { indicator.setFraction(1.0); } } @Override public void cleanup(@NotNull Return result) { // Do nothing } } }