io.permazen.jsck.Issue Maven / Gradle / Ivy
Show all versions of permazen-jsck Show documentation
/*
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
*/
package io.permazen.jsck;
import com.google.common.base.Preconditions;
import io.permazen.core.ObjId;
import io.permazen.kv.KVStore;
import io.permazen.schema.SchemaCompositeIndex;
import io.permazen.schema.SchemaField;
import io.permazen.util.ByteUtil;
import io.permazen.util.ParseContext;
import java.util.Arrays;
import java.util.Objects;
/**
* Represents a point of corruption or inconsistency in a Permazen key/value database.
*
*
* {@link Issue}s are specific to a single key/value pair in the database.
*/
public abstract class Issue {
private static final int HEX_STRING_LIMIT = 100;
private final String description;
private final byte[] key;
private final byte[] oldValue;
private final byte[] newValue;
private String detail;
/**
* Constructor.
*
* @param description short description of the issue
* @param key key of invalid key/value pair
* @param oldValue the original, invalid value for {@code key}, or null if {@code key} was missing or don't care
* @param newValue the corrected replacement value for {@code key}, or null if {@code key} should be deleted
* @throws IllegalArgumentException if {@code key} is null
* @throws IllegalArgumentException if {@code oldValue} and {@code newValue} are equal
*/
protected Issue(String description, byte[] key, byte[] oldValue, byte[] newValue) {
Preconditions.checkArgument(description != null, "null description");
Preconditions.checkArgument(key != null, "null key");
Preconditions.checkArgument(!Arrays.equals(oldValue, newValue) || oldValue == null, "newValue = oldValue");
this.description = description;
this.key = key;
this.oldValue = oldValue;
this.newValue = newValue;
}
/**
* Get the databse key.
*
* @return key of this issue
*/
public byte[] getKey() {
return this.key;
}
/**
* Get the original value, if any.
*
* @return original value, null if key/value pair was missing
*/
public byte[] getOldValue() {
return this.oldValue;
}
/**
* Get the replacement value, if any.
*
* @return replacement value, null if key/value pair was missing or should be deleted
*/
public byte[] getNewValue() {
return this.newValue;
}
/**
* Apply the fix for this issue to the given {@link KVStore}.
*
*
* This overwrites the key/value pair with the new value, or deletes it if {@link #getNewValue} is null.
*
* @param kvstore target key/value store
* @throws IllegalArgumentException if {@code kvstore} is null
*/
public void apply(KVStore kvstore) {
Preconditions.checkArgument(kvstore != null, "null kvstore");
if (this.newValue != null)
kvstore.put(this.key, this.newValue);
else
kvstore.remove(this.key);
}
public Issue setDetail(String detail) {
this.detail = detail;
return this;
}
public Issue setDetail(ObjId id, String detail) {
return this.setDetail("for object " + id + ": " + detail);
}
public Issue setDetail(ObjId id, SchemaField field, String detail) {
return this.setDetail("for object " + id + " " + field + ": " + detail);
}
public Issue setDetail(ObjId id, SchemaCompositeIndex index, String detail) {
return this.setDetail("for object " + id + " " + index + ": " + detail);
}
public Issue setDetail(Index index, String detail) {
return this.setDetail("for " + index + ": " + detail);
}
// Object
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append(this.description)
.append(": key [")
.append(ParseContext.truncate(ByteUtil.toString(this.key), HEX_STRING_LIMIT))
.append(']');
if (this.oldValue != null && this.newValue != null) {
buf.append(": incorrect value [")
.append(ParseContext.truncate(ByteUtil.toString(this.oldValue), HEX_STRING_LIMIT))
.append("]; corrected value [")
.append(ParseContext.truncate(ByteUtil.toString(this.newValue), HEX_STRING_LIMIT))
.append(']');
} else if (this.oldValue != null) {
buf.append(", value [")
.append(ParseContext.truncate(ByteUtil.toString(this.oldValue), HEX_STRING_LIMIT))
.append(']');
} else if (this.newValue != null && this.newValue.length > 0) {
buf.append(", corrected value [")
.append(ParseContext.truncate(ByteUtil.toString(this.newValue), HEX_STRING_LIMIT))
.append(']');
}
if (this.detail != null) {
buf.append(": ")
.append(this.detail);
}
return buf.toString();
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != this.getClass())
return false;
final Issue that = (Issue)obj;
return Arrays.equals(this.key, that.key)
&& Arrays.equals(this.oldValue, that.oldValue)
&& Arrays.equals(this.newValue, that.newValue)
&& Objects.equals(this.description, that.description)
&& Objects.equals(this.detail, that.detail);
}
@Override
public int hashCode() {
return Arrays.hashCode(this.key)
^ Arrays.hashCode(this.oldValue)
^ Arrays.hashCode(this.newValue)
^ Objects.hashCode(this.description)
^ Objects.hashCode(this.detail);
}
}