/******************************************************************************* * * Copyright (c) 2004-2010, Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * * * *******************************************************************************/ package hudson.console; import hudson.MarkupText; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.ListIterator; /** * Annotates one line of console output. * * <p> In Hudson, console output annotation is done line by line, and we model * this as a state machine — the code encapsulates some state, and it uses * that to annotate one line (and possibly update the state.) * * <p> A {@link ConsoleAnnotator} instance encapsulates this state, and the * {@link #annotate(Object, MarkupText)} method is used to annotate the next * line based on the current state. The method returns another * {@link ConsoleAnnotator} instance that represents the altered state for * annotating the next line. * * <p> {@link ConsoleAnnotator}s are run when a browser requests console output, * and the above-mentioned chain invocation is done for each client request * separately. Therefore, logically you can think of this process as: * * <pre> * ConsoleAnnotator ca = ...; * ca.annotate(context,line1).annotate(context,line2)... * </pre> * * <p> Because of a browser can request console output incrementally, in * addition to above a console annotator can be serialized at any point and * deserialized back later to continue annotation where it left off. * * <p> {@link ConsoleAnnotator} instances can be created in a few different * ways. See {@link ConsoleNote} and {@link ConsoleAnnotatorFactory}. * * @author Kohsuke Kawaguchi * @see ConsoleAnnotatorFactory * @see ConsoleNote * @since 1.349 */ public abstract class ConsoleAnnotator<T> implements Serializable { /** * Annotates one line. * * @param context The object that owns the console output. Never null. * @param text Contains a single line of console output, and defines * convenient methods to add markup. The callee should put markup into this * object. Never null. * @return The {@link ConsoleAnnotator} object that will annotate the next * line of the console output. To indicate that you are not interested in * the following lines, return null. */ public abstract ConsoleAnnotator annotate(T context, MarkupText text); /** * Cast operation that restricts T. */ public static <T> ConsoleAnnotator<T> cast(ConsoleAnnotator<? super T> a) { return (ConsoleAnnotator) a; } /** * Bundles all the given {@link ConsoleAnnotator} into a single annotator. */ public static <T> ConsoleAnnotator<T> combine(Collection<? extends ConsoleAnnotator<? super T>> all) { switch (all.size()) { case 0: return null; // none case 1: return cast(all.iterator().next()); // just one } class Aggregator extends ConsoleAnnotator<T> { List<ConsoleAnnotator<T>> list; Aggregator(Collection list) { this.list = new ArrayList<ConsoleAnnotator<T>>(list); } public ConsoleAnnotator annotate(T context, MarkupText text) { ListIterator<ConsoleAnnotator<T>> itr = list.listIterator(); while (itr.hasNext()) { ConsoleAnnotator a = itr.next(); ConsoleAnnotator b = a.annotate(context, text); if (a != b) { if (b == null) { itr.remove(); } else { itr.set(b); } } } switch (list.size()) { case 0: return null; // no more annotator left case 1: return list.get(0); // no point in aggregating default: return this; } } } return new Aggregator(all); } /** * Returns the all {@link ConsoleAnnotator}s for the given context type * aggregated into a single annotator. */ public static <T> ConsoleAnnotator<T> initial(T context) { return combine(_for(context)); } /** * List all the console annotators that can work for the specified context * type. */ public static <T> List<ConsoleAnnotator<T>> _for(T context) { List<ConsoleAnnotator<T>> r = new ArrayList<ConsoleAnnotator<T>>(); for (ConsoleAnnotatorFactory f : ConsoleAnnotatorFactory.all()) { if (f.type().isInstance(context)) { ConsoleAnnotator ca = f.newInstance(context); if (ca != null) { r.add(ca); } } } return r; } private static final long serialVersionUID = 1L; }