com.google.dart.tools.ui.internal.refactoring.ServerRefactoring.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.tools.ui.internal.refactoring.ServerRefactoring.java

Source

/*
 * Copyright (c) 2014, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
 * 
 * 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.google.dart.tools.ui.internal.refactoring;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.dart.server.GetRefactoringConsumer;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.internal.corext.refactoring.base.StringStatusContext;

import static com.google.dart.tools.ui.internal.refactoring.ServiceUtils_NEW.toLTK;
import static com.google.dart.tools.ui.internal.refactoring.ServiceUtils_NEW.toRefactoringStatus;

import org.apache.commons.lang3.StringUtils;
import org.dartlang.analysis.server.protocol.RefactoringFeedback;
import org.dartlang.analysis.server.protocol.RefactoringOptions;
import org.dartlang.analysis.server.protocol.RefactoringProblem;
import org.dartlang.analysis.server.protocol.RequestError;
import org.dartlang.analysis.server.protocol.SourceChange;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;

import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * LTK wrapper around Analysis Server refactoring.
 * 
 * @coverage dart.editor.ui.refactoring.ui
 */
public abstract class ServerRefactoring extends Refactoring {
    public interface ServerRefactoringListener {
        void requestStateChanged(boolean hasPendingRequests, RefactoringStatus optionsStatus);
    }

    protected static final RefactoringStatus TIMEOUT_STATUS = RefactoringStatus.createFatalErrorStatus("Timeout");

    public static String[] toStringArray(List<String> list) {
        return list.toArray(new String[list.size()]);
    }

    protected final String kind;
    private final String name;
    private final String file;
    private final int offset;
    private final int length;

    protected RefactoringStatus serverErrorStatus;
    protected RefactoringStatus initialStatus;
    protected RefactoringStatus optionsStatus;
    protected RefactoringStatus finalStatus;
    private Change change;
    private final List<String> externalFiles = Lists.newArrayList();

    private int lastId = 0;
    private final Set<Integer> pendingRequestIds = Sets.newHashSet();
    private ServerRefactoringListener listener;

    public ServerRefactoring(String kind, String name, String file, int offset, int length) {
        this.kind = kind;
        this.name = name;
        this.file = file;
        this.offset = offset;
        this.length = length;
    }

    @Override
    public RefactoringStatus checkFinalConditions(IProgressMonitor pm) {
        setOptions(false, pm);
        if (serverErrorStatus != null) {
            return serverErrorStatus;
        }
        // done if already fatal
        if (finalStatus.hasFatalError()) {
            return finalStatus;
        }
        // check for external files
        if (!externalFiles.isEmpty()) {
            finalStatus.addError("The following files are external and cannot be updated",
                    new StringStatusContext(null, StringUtils.join(externalFiles, "\n")));
        }
        // done
        return finalStatus;
    }

    @Override
    public RefactoringStatus checkInitialConditions(IProgressMonitor pm) {
        setOptions(true, pm);
        if (serverErrorStatus != null) {
            return serverErrorStatus;
        }
        return initialStatus;
    }

    @Override
    public Change createChange(IProgressMonitor pm) {
        setOptions(false, pm);
        return change;
    }

    @Override
    public String getName() {
        return name;
    }

    /**
     * @return {@code true} if the {@link Change} created by refactoring may be unsafe, so we want
     *         user to review the change to ensure that he understand it.
     */
    public boolean requiresPreview() {
        return false;
    }

    public void setListener(ServerRefactoringListener listener) {
        this.listener = listener;
    }

    /**
     * Returns this {@link RefactoringOptions} subclass instance.
     */
    protected abstract RefactoringOptions getOptions();

    /**
     * Sets the received {@link RefactoringFeedback}.
     */
    protected abstract void setFeedback(RefactoringFeedback feedback);

    protected void setOptions(boolean validateOnly) {
        setOptions(validateOnly, null);
    }

    protected void setOptions(boolean validateOnly, IProgressMonitor pm) {
        // add a new pending request ID
        final int id;
        synchronized (pendingRequestIds) {
            id = ++lastId;
            pendingRequestIds.add(id);
        }
        // do request
        serverErrorStatus = null;
        final CountDownLatch latch = new CountDownLatch(1);
        RefactoringOptions options = getOptions();
        DartCore.getAnalysisServer().edit_getRefactoring(kind, file, offset, length, validateOnly, options,
                new GetRefactoringConsumer() {
                    @Override
                    public void computedRefactorings(List<RefactoringProblem> initialProblems,
                            List<RefactoringProblem> optionsProblems, List<RefactoringProblem> finalProblems,
                            RefactoringFeedback feedback, SourceChange _change, List<String> potentialEdits) {
                        if (feedback != null) {
                            setFeedback(feedback);
                        }
                        externalFiles.clear();
                        initialStatus = toRefactoringStatus(initialProblems);
                        optionsStatus = toRefactoringStatus(optionsProblems);
                        finalStatus = toRefactoringStatus(finalProblems);
                        change = toLTK(_change, externalFiles);
                        latch.countDown();
                        requestDone(id);
                    }

                    @Override
                    public void onError(RequestError requestError) {
                        String message = "Server error: " + requestError.getMessage();
                        serverErrorStatus = RefactoringStatus.createFatalErrorStatus(message);
                        latch.countDown();
                        requestDone(id);
                    }

                    private void requestDone(final int id) {
                        synchronized (pendingRequestIds) {
                            pendingRequestIds.remove(id);
                            notifyListener();
                        }
                    }
                });
        // wait for completion
        if (pm != null) {
            while (true) {
                if (pm.isCanceled()) {
                    throw new OperationCanceledException();
                }
                boolean done = Uninterruptibles.awaitUninterruptibly(latch, 10, TimeUnit.MILLISECONDS);
                if (done) {
                    return;
                }
            }
        } else {
            // Wait a very short time, just in case it it can be done fast,
            // so that we don't have to disable UI and re-enable it 2 milliseconds later.
            Uninterruptibles.awaitUninterruptibly(latch, 10, TimeUnit.MILLISECONDS);
            notifyListener();
        }
    }

    private void notifyListener() {
        if (listener != null) {
            boolean hasPendingRequests = !pendingRequestIds.isEmpty();
            listener.requestStateChanged(hasPendingRequests, optionsStatus);
        }
    }
}