/* * Copyright 2000-2010 JetBrains s.r.o. * 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 org.community.intellij.plugins.communitycase.history.wholeTree; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.CalledInAwt; import com.intellij.util.containers.Convertor; import com.intellij.util.continuation.*; import org.jetbrains.annotations.Nullable; import java.util.List; /** * @author irengrig */ public class LoadAlgorithm { private static final long ourTestTimeThreshold = 10000; private final Project myProject; private final List<LoaderAndRefresher<CommitHashPlusParents>> myLoaders; private final List<String> myAbstractHashs; private Continuation myContinuation; public LoadAlgorithm(final Project project, final List<LoaderAndRefresher<CommitHashPlusParents>> loaders, final List<String> abstractHashs) { myProject = project; myLoaders = loaders; myAbstractHashs = abstractHashs; myContinuation = Continuation.createFragmented(myProject, false); } public void execute() { final GatheringContinuationContext initContext = new GatheringContinuationContext(); if (myAbstractHashs != null) { initContext.last(new TryHashes()); } for (LoaderAndRefresher<CommitHashPlusParents> loader : myLoaders) { final LoaderFactory factory = new LoaderFactory(loader); final State state = new State(factory); state.scheduleSelf(initContext); } myContinuation.run(initContext.getList()); } @CalledInAwt public void stop() { for (LoaderAndRefresher loader : myLoaders) { loader.interrupt(); } } private class TryHashes extends TaskDescriptor { private TryHashes() { super("Try load by hashes", Where.POOLED); } @Override public void run(ContinuationContext context) { for (LoaderAndRefresher loader : myLoaders) { loader.loadByHashesAside(myAbstractHashs); } } } private static class LoadTaskDescriptor extends TaskDescriptor { protected final State myState; private final LoaderAndRefresher<CommitHashPlusParents> myLoader; private final RefreshTaskDescriptor myRefreshTaskDescriptor; protected LoadTaskDescriptor(final State state, final LoaderAndRefresher<CommitHashPlusParents> loader, final RefreshTaskDescriptor refreshTaskDescriptor) { super("Load tree skeleton", Where.POOLED); myState = state; myLoader = loader; myRefreshTaskDescriptor = refreshTaskDescriptor; } protected void processResult(final Result result) { } @Override public void run(final ContinuationContext context) { final Result<CommitHashPlusParents> result = myLoader.load(myState.myValue, myState.getContinuationTs()); processResult(result); myState.setContinuationTs(result.getLast() == null ? -1 : result.getLast().getTime()); if (! result.isIsComplete()) { // no next stage if it is completed myState.transition(); if (! myLoader.isInterrupted()) { myState.scheduleSelf(context); } context.next(myRefreshTaskDescriptor); } else { context.next(myRefreshTaskDescriptor); } } } private static class TestLoadTaskDescriptor extends LoadTaskDescriptor { private TestLoadTaskDescriptor(final State state, final LoaderAndRefresher loader, final RefreshTaskDescriptor refreshTaskDescriptor) { super(state, loader, refreshTaskDescriptor); } @Override protected void processResult(Result result) { assert LoadType.TEST.equals(myState.myValue); myState.takeDecision(result.getTime() > ourTestTimeThreshold); } } private static class RefreshTaskDescriptor extends TaskDescriptor { private final LoaderAndRefresher myUiRefresh; private RefreshTaskDescriptor(final LoaderAndRefresher uiRefresh) { super("", Where.AWT); myUiRefresh = uiRefresh; } @Override public void run(final ContinuationContext context) { final boolean toContinue = myUiRefresh.flushIntoUI(); if (! toContinue) context.cancelEverything(); } } public static enum LoadType { TEST(true, false), FULL_START(false, true), FULL(false, true), FULL_PREVIEW(true, true), SHORT_START(false, true), SHORT(false, true); private final boolean myStartEarly; private final boolean myUsesContinuation; private LoadType(final boolean startEarly, boolean startsContinuation) { myStartEarly = startEarly; myUsesContinuation = startsContinuation; } public boolean isStartEarly() { return myStartEarly; } public boolean isUsesContinuation() { return myUsesContinuation; } } public static class Result<T> { private final long myTime; private final boolean myIsComplete; private final T myLast; public Result(boolean isComplete, long time, final T t) { myIsComplete = isComplete; myTime = time; myLast = t; } public boolean isIsComplete() { return myIsComplete; } public long getTime() { return myTime; } public T getLast() { return myLast; } } private static class LoaderFactory implements Convertor<State, LoadTaskDescriptor> { private final LoaderAndRefresher<CommitHashPlusParents> myLoader; private final RefreshTaskDescriptor myRefreshTaskDescriptor; private LoaderFactory(final LoaderAndRefresher<CommitHashPlusParents> loader) { myLoader = loader; myRefreshTaskDescriptor = new RefreshTaskDescriptor(loader); } @Override public LoadTaskDescriptor convert(final State state) { if (LoadType.TEST.equals(state.myValue)) { return new TestLoadTaskDescriptor(state, myLoader, myRefreshTaskDescriptor); } return new LoadTaskDescriptor(state, myLoader, myRefreshTaskDescriptor); } } // pseudo enum private class State { private boolean myLoadFull; private long myContinuationTs; @Nullable private LoadType myValue; private final Convertor<State, LoadTaskDescriptor> myLoaderFactory; private State(final Convertor<State, LoadTaskDescriptor> loaderFactory) { myContinuationTs = -1; myLoaderFactory = loaderFactory; myValue = LoadType.TEST; myLoadFull = false; } public void scheduleSelf(final ContinuationContext context) { if (myValue == null) return; if (myValue.isStartEarly()) { context.next(myLoaderFactory.convert(this)); } else { context.last(myLoaderFactory.convert(this)); } } public long getContinuationTs() { return myContinuationTs; } public void setContinuationTs(long continuationTs) { myContinuationTs = continuationTs; } public void takeDecision(final boolean loadFull) { myLoadFull = loadFull; } public void transition() { if (LoadType.TEST.equals(myValue)) { myValue = myLoadFull ? LoadType.FULL_START : LoadType.FULL_PREVIEW; } else if (LoadType.FULL_PREVIEW.equals(myValue)) { myValue = LoadType.SHORT_START; } else if (LoadType.FULL_START.equals(myValue)) { myValue = LoadType.FULL; } else if (LoadType.SHORT_START.equals(myValue)) { myValue = LoadType.SHORT; } else if (LoadType.SHORT.equals(myValue)) { myValue = LoadType.SHORT; } else if (LoadType.FULL.equals(myValue)) { myValue = LoadType.FULL; } else { myValue = null; } } } }