Java tutorial
/* * Copyright 2012-present Facebook, Inc. * * 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.facebook.buck.rules; import com.google.common.hash.HashCode; import com.google.common.hash.Hashing; import java.io.File; import java.nio.ByteBuffer; import javax.annotation.Nullable; /** * OutputKey encapsulates computation of SHA-1 content hashing for outputs. */ public class OutputKey implements Comparable<OutputKey> { @Nullable private final HashCode hashCode; private final boolean idempotent; /** * OutputKeys that have no associated outputs (output == null) are treated as sentinels that are * distinct from other idempotent OutputKeys. OutputKeys associated with actual outputs are * non-idempotent if the outputs cannot be read during key generation. */ public OutputKey(@Nullable File output) { if (output == null) { this.hashCode = null; this.idempotent = true; } else { this.hashCode = hashOutput(output); this.idempotent = (hashCode != null); } } @Nullable private HashCode hashOutput(File output) { if (output == null) { return null; } // Use RuleKey's Builder to do the hard work of hashing the output file. RuleKey.Builder builder = RuleKey.builder(); builder.set(output.getPath(), output); RuleKey ruleKey = builder.build(); if (!ruleKey.isIdempotent()) { return null; } return ruleKey.getHashCode(); } public boolean isIdempotent() { return idempotent; } public boolean isSentinel() { return (isIdempotent() && hashCode == null); } /** * Sentinel OutputKeys are output as a string of 's' characters, and non-idempotent OutputKeys are * normally output as a string of 'x' characters, but when comparing two sets of OutputKeys in * textual form it is necessary to mangle one of the two sets, so that non-idempotent OutputKeys * are never considered equal. */ public String toString(boolean mangleNonIdempotent) { if (isSentinel() || !isIdempotent()) { String replacementChar = isSentinel() ? "s" : (!mangleNonIdempotent ? "x" : "y"); return new String(new char[Hashing.sha1().bits() / 4]).replace("\0", replacementChar); } return hashCode.toString(); } @Override public String toString() { return toString(false); } /** * Order non-idempotent OutputKeys as less than sentinels, which in turn are less than all * idempotent non-sentinels. * * non-idempotent < sentinel < idempotent non-sentinel */ @Override public int compareTo(OutputKey other) { // Handle non-idempotent keys. if (!isIdempotent()) { if (!other.isIdempotent()) { return 0; } return -1; } else if (!other.isIdempotent()) { return 1; } // Handle sentinel keys. if (isSentinel()) { if (other.isSentinel()) { return 0; } return -1; } else if (other.isSentinel()) { return 1; } // Handle idempotent non-sentinel keys. return ByteBuffer.wrap(hashCode.asBytes()).compareTo(ByteBuffer.wrap(other.hashCode.asBytes())); } /** * Treat non-idempotent OutputKeys as unequal to everything, including other non-idempotent * OutputKeys. */ @Override public boolean equals(Object that) { if (!(that instanceof OutputKey)) { return false; } OutputKey other = (OutputKey) that; if (!isIdempotent() || !other.isIdempotent()) { return false; } return (compareTo(other) == 0); } @Override public int hashCode() { if (!isIdempotent()) { return 0; } return super.hashCode(); } /** * Helper method used to avoid memoizing non-idempotent OutputKeys. */ public static OutputKey filter(OutputKey outputKey) { if (!outputKey.isIdempotent()) { return null; } return outputKey; } }