/* * 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.engine.internal.context; import com.google.dart.engine.source.Source; import java.util.ArrayList; import java.util.NoSuchElementException; /** * Instances of the class {@code WorkManager} manage a list of sources that need to have analysis * work performed on them. */ public class WorkManager { /** * Instances of the class {@code WorkIterator} implement an iterator that returns the sources in a * work manager in the order in which they are to be analyzed. */ public class WorkIterator { /** * The index of the work queue through which we are currently iterating. */ private int queueIndex = 0; /** * The index of the next element of the work queue to be returned. */ private int index = -1; /** * Initialize a newly created iterator to be ready to return the first element in the iteration. */ public WorkIterator() { advance(); } /** * Return {@code true} if there is another {@link Source} available for processing. * * @return {@code true} if there is another {@link Source} available for processing */ public boolean hasNext() { return queueIndex < workQueues.length; } /** * Return the next {@link Source} available for processing and advance so that the returned * source will not be returned again. * * @return the next {@link Source} available for processing */ public Source next() { if (!hasNext()) { throw new NoSuchElementException(); } Source source = workQueues[queueIndex].get(index); advance(); return source; } /** * Increment the {@link #index} and {@link #queueIndex} so that they are either indicating the * next source to be returned or are indicating that there are no more sources to be returned. */ private void advance() { index++; if (index >= workQueues[queueIndex].size()) { index = 0; queueIndex++; while (queueIndex < workQueues.length && workQueues[queueIndex].isEmpty()) { queueIndex++; } } } } /** * An array containing the various queues is priority order. */ private ArrayList<Source>[] workQueues; /** * Initialize a newly created manager to have no work queued up. */ @SuppressWarnings("unchecked") public WorkManager() { int queueCount = SourcePriority.values().length; workQueues = new ArrayList[queueCount]; for (int i = 0; i < queueCount; i++) { workQueues[i] = new ArrayList<Source>(); } } /** * Record that the given source needs to be analyzed. The priority level is used to control when * the source will be analyzed with respect to other sources. If the source was previously added * then it's priority is updated. If it was previously added with the same priority then it's * position in the queue is unchanged. * * @param source the source that needs to be analyzed * @param priority the priority level of the source */ public void add(Source source, SourcePriority priority) { int queueCount = workQueues.length; int ordinal = priority.ordinal(); for (int i = 0; i < queueCount; i++) { ArrayList<Source> queue = workQueues[i]; if (i == ordinal) { if (!queue.contains(source)) { queue.add(source); } } else { queue.remove(source); } } } /** * Record that the given source needs to be analyzed. The priority level is used to control when * the source will be analyzed with respect to other sources. If the source was previously added * then it's priority is updated. In either case, it will be analyzed before other sources of the * same priority. * * @param source the source that needs to be analyzed * @param priority the priority level of the source */ public void addFirst(Source source, SourcePriority priority) { int queueCount = workQueues.length; int ordinal = priority.ordinal(); for (int i = 0; i < queueCount; i++) { ArrayList<Source> queue = workQueues[i]; if (i == ordinal) { queue.remove(source); queue.add(0, source); } else { queue.remove(source); } } } /** * Return an iterator that can be used to access the sources to be analyzed in the order in which * they should be analyzed. * <p> * <b>Note:</b> As with other iterators, no sources can be added or removed from this work manager * while the iterator is being used. Unlike some implementations, however, the iterator will not * detect when this requirement has been violated; it might work correctly, it might return the * wrong source, or it might throw an exception. * * @return an iterator that can be used to access the next source to be analyzed */ public WorkIterator iterator() { return new WorkIterator(); } /** * Record that the given source is fully analyzed. * * @param source the source that is fully analyzed */ public void remove(Source source) { int queueCount = workQueues.length; for (int i = 0; i < queueCount; i++) { workQueues[i].remove(source); } } @Override public String toString() { StringBuilder builder = new StringBuilder(); SourcePriority[] priorities = SourcePriority.values(); boolean needsSeparator = false; int queueCount = workQueues.length; for (int i = 0; i < queueCount; i++) { ArrayList<Source> queue = workQueues[i]; if (!queue.isEmpty()) { if (needsSeparator) { builder.append("; "); } builder.append(priorities[i]); builder.append(": "); int queueSize = queue.size(); for (int j = 0; j < queueSize; j++) { if (j > 0) { builder.append(", "); } builder.append(queue.get(j).getFullName()); } needsSeparator = true; } } return builder.toString(); } }