|  | // Copyright 2014 The Bazel Authors. All rights reserved. | 
|  | // | 
|  | // 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.google.devtools.build.lib.events; | 
|  |  | 
|  | import static java.nio.charset.StandardCharsets.UTF_8; | 
|  |  | 
|  | import com.google.common.base.Preconditions; | 
|  | import java.io.Serializable; | 
|  | import java.util.Arrays; | 
|  | import java.util.Objects; | 
|  | import javax.annotation.Nullable; | 
|  | import javax.annotation.concurrent.Immutable; | 
|  |  | 
|  | /** | 
|  | * An event is a situation encountered by the build system that's worth | 
|  | * reporting: A 3-tuple of ({@link EventKind}, {@link Location}, message). | 
|  | */ | 
|  | @Immutable | 
|  | public final class Event implements Serializable { | 
|  | private final EventKind kind; | 
|  | private final Location location; | 
|  | private final String message; | 
|  | @Nullable private final String stdout; | 
|  | @Nullable private final String stderr; | 
|  |  | 
|  | /** | 
|  | * An alternative representation for message. | 
|  | * | 
|  | * <p>Exactly one of message or messageBytes will be non-null. If messageBytes is non-null, then | 
|  | * it contains the UTF-8-encoded bytes of the message. We do this to avoid converting back and | 
|  | * forth between Strings and bytes. | 
|  | */ | 
|  | private final byte[] messageBytes; | 
|  |  | 
|  | @Nullable | 
|  | private final String tag; | 
|  |  | 
|  | private final int hashCode; | 
|  |  | 
|  | private Event( | 
|  | EventKind kind, | 
|  | @Nullable Location location, | 
|  | String message, | 
|  | @Nullable String tag, | 
|  | @Nullable String stdout, | 
|  | @Nullable String stderr) { | 
|  | this.kind = Preconditions.checkNotNull(kind); | 
|  | this.location = location; | 
|  | this.message = Preconditions.checkNotNull(message); | 
|  | this.messageBytes = null; | 
|  | this.tag = tag; | 
|  | this.stdout = stdout; | 
|  | this.stderr = stderr; | 
|  | this.hashCode = | 
|  | Objects.hash(kind, location, message, tag, Arrays.hashCode(messageBytes), stdout, stderr); | 
|  | } | 
|  |  | 
|  | private Event( | 
|  | EventKind kind, | 
|  | @Nullable Location location, | 
|  | byte[] messageBytes, | 
|  | @Nullable String tag, | 
|  | @Nullable String stdout, | 
|  | @Nullable String stderr) { | 
|  | this.kind = Preconditions.checkNotNull(kind); | 
|  | this.location = location; | 
|  | this.message = null; | 
|  | this.messageBytes = Preconditions.checkNotNull(messageBytes); | 
|  | this.tag = tag; | 
|  | this.stdout = stdout; | 
|  | this.stderr = stderr; | 
|  | this.hashCode = | 
|  | Objects.hash(kind, location, message, tag, Arrays.hashCode(messageBytes), stdout, stderr); | 
|  | } | 
|  |  | 
|  | public Event withTag(String tag) { | 
|  | if (Objects.equals(tag, this.tag)) { | 
|  | return this; | 
|  | } | 
|  | if (this.message != null) { | 
|  | return new Event(this.kind, this.location, this.message, tag, this.stdout, this.stderr); | 
|  | } else { | 
|  | return new Event(this.kind, this.location, this.messageBytes, tag, this.stdout, this.stderr); | 
|  | } | 
|  | } | 
|  |  | 
|  | public Event withStdoutStderr(String stdout, String stderr) { | 
|  | if (this.message != null) { | 
|  | return new Event(this.kind, this.location, this.message, this.tag, stdout, stderr); | 
|  | } else { | 
|  | return new Event(this.kind, this.location, this.messageBytes, this.tag, stdout, stderr); | 
|  | } | 
|  | } | 
|  |  | 
|  | public String getMessage() { | 
|  | return message != null ? message : new String(messageBytes, UTF_8); | 
|  | } | 
|  |  | 
|  | public byte[] getMessageBytes() { | 
|  | return messageBytes != null ? messageBytes : message.getBytes(UTF_8); | 
|  | } | 
|  |  | 
|  | public EventKind getKind() { | 
|  | return kind; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * the tag is typically the action that generated the event. | 
|  | */ | 
|  | @Nullable | 
|  | public String getTag() { | 
|  | return tag; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get the stdout bytes associated with this event; typically, the event will report where the | 
|  | * output originated from. | 
|  | */ | 
|  | @Nullable | 
|  | public String getStdOut() { | 
|  | return stdout; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get the stdout bytes associated with this event; typically, the event will report where the | 
|  | * output originated from. | 
|  | */ | 
|  | @Nullable | 
|  | public String getStdErr() { | 
|  | return stderr; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the location of this event, if any.  Returns null iff the event | 
|  | * wasn't associated with any particular location, for example, a progress | 
|  | * message. | 
|  | */ | 
|  | @Nullable public Location getLocation() { | 
|  | return location; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns <i>some</i> moderately sane representation of the event. Should never be used in | 
|  | * user-visible places, only for debugging and testing. | 
|  | */ | 
|  | @Override | 
|  | public String toString() { | 
|  | return kind + " " + (location != null ? location.print() : "<no location>") + ": " | 
|  | + getMessage(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return hashCode; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | if (other == null || !other.getClass().equals(getClass())) { | 
|  | return false; | 
|  | } | 
|  | Event that = (Event) other; | 
|  | return Objects.equals(this.kind, that.kind) | 
|  | && Objects.equals(this.location, that.location) | 
|  | && Objects.equals(this.tag, that.tag) | 
|  | && Objects.equals(this.message, that.message) | 
|  | && Objects.equals(this.stdout, that.stdout) | 
|  | && Objects.equals(this.stderr, that.stderr) | 
|  | && Arrays.equals(this.messageBytes, that.messageBytes); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Replay a sequence of events on an {@link EventHandler}. | 
|  | */ | 
|  | public static void replayEventsOn(EventHandler eventHandler, Iterable<Event> events) { | 
|  | for (Event event : events) { | 
|  | eventHandler.handle(event); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static Event of(EventKind kind, @Nullable Location location, String message) { | 
|  | return new Event(kind, location, message, null, null, null); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Construct an event by passing in the {@code byte[]} array instead of a String. | 
|  | * | 
|  | * The bytes must be decodable as UTF-8 text. | 
|  | */ | 
|  | public static Event of(EventKind kind, @Nullable Location location, byte[] messageBytes) { | 
|  | return new Event(kind, location, messageBytes, null, null, null); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reports an error. | 
|  | */ | 
|  | public static Event error(@Nullable Location location, String message){ | 
|  | return new Event(EventKind.ERROR, location, message, null, null, null); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reports a warning. | 
|  | */ | 
|  | public static Event warn(@Nullable Location location, String message) { | 
|  | return new Event(EventKind.WARNING, location, message, null, null, null); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reports atemporal statements about the build, i.e. they're true for the duration of execution. | 
|  | */ | 
|  | public static Event info(@Nullable Location location, String message) { | 
|  | return new Event(EventKind.INFO, location, message, null, null, null); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reports a temporal statement about the build. | 
|  | */ | 
|  | public static Event progress(@Nullable Location location, String message) { | 
|  | return new Event(EventKind.PROGRESS, location, message, null, null, null); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reports a debug message. | 
|  | */ | 
|  | public static Event debug(@Nullable Location location, String message) { | 
|  | return new Event(EventKind.DEBUG, location, message, null, null, null); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reports an error. | 
|  | */ | 
|  | public static Event error(String message){ | 
|  | return error(null, message); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reports a warning. | 
|  | */ | 
|  | public static Event warn(String message) { | 
|  | return warn(null, message); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reports atemporal statements about the build, i.e. they're true for the duration of execution. | 
|  | */ | 
|  | public static Event info(String message) { | 
|  | return info(null, message); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reports a temporal statement about the build. | 
|  | */ | 
|  | public static Event progress(String message) { | 
|  | return progress(null, message); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reports a debug message. | 
|  | */ | 
|  | public static Event debug(String message) { | 
|  | return debug(null, message); | 
|  | } | 
|  | } |