package reactor.pipe.key;

import java.util.Arrays;
import java.util.UUID;

public class Key {

  private final Object[] parts;
  private final boolean isDerived;
  private volatile Object metadata;

  public Key(Object[] parts) {
    this(parts, false);
  }

  protected Key(Object[] parts, boolean isDerived) {
    this.parts = parts;
    this.isDerived = isDerived;
  }

  @SuppressWarnings("unchecked")
  public <T> void setMetadata(T metadata) {
    this.metadata = metadata;
  }

  @SuppressWarnings("unchecked")
  public <T> T getMetadata() {
    return (T) metadata;
  }

  public Key derive() {
    Object[] newKey = new Object[parts.length + 1];
    System.arraycopy(parts, 0, newKey, 0, parts.length);
    newKey[parts.length] = UUID.randomUUID();
    return new Key(newKey, true);
  }

  public Object getPart(int index) {
    if (index > parts.length - 1) {
      throw new RuntimeException(String.format("Can't get a part with index %d from %d long key",
                                               index,
                                               parts.length));
    } else {
      return parts[index];
    }
  }

  public boolean isDerivedFrom(Key other) {
    if (!isDerived || (this.parts.length <= other.parts.length)) {
      return false;
    }

    for(int i = 0; i < other.parts.length; i++) {
      if (!(other.parts[i].equals(this.parts[i]))) {
        return false;
      }
    }
    return true;
  }

  public boolean isDerived() {
    return isDerived;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o)
      return true;
    if (o == null || getClass() != o.getClass())
      return false;

    Key key = (Key) o;

    // Probably incorrect - comparing Object[] arrays with Arrays.equals
    if (!Arrays.equals(parts, key.parts))
      return false;

    return true;
  }

  @Override
  public int hashCode() {
    return parts != null ? Arrays.hashCode(parts) : 0;
  }

  public static Key wrap(Object k) {
    return new Key(new Object[] { k } );
  }

  public static Key wrap(Object... k) {
    return new Key(k);
  }

  @Override
  public String toString() {
    return "Key{" +
           "parts=" + Arrays.toString(parts) +
           '}';
  }

  public Object[] getObjects() {
    return Arrays.copyOf(parts, parts.length);
  }
}


@@ -245,16 +245,22 @@
 s, false
+, null
 );%0A  %7D%0A%0A
@@ -308,16 +308,33 @@
 sDerived
+, Object metadata
 ) %7B%0A    
@@ -377,32 +377,62 @@
 ed = isDerived;%0A
+    this.metadata = metadata;%0A
   %7D%0A%0A  @Suppress
@@ -836,23 +836,66 @@
 ey, true
-);%0A  %7D%0A
+, metadata);%0A  %7D%0A%0A  @SuppressWarnings(%22unchecked%22)
 %0A  publi
@@ -896,22 +896,21 @@
  public 
-Object
+%3CT%3E T
  getPart
@@ -1195,24 +1195,28 @@
       return
+ (T)
  parts%5Bindex

Add metadata to Key