public FileAttributes(Option<String> user, Option<String> group, Option<UnixFileMode> mode) {
   this(user, group, mode, List.<String>nil());
 }
/** @author <a href="mailto:[email protected]">Trygve Laugst&oslash;l</a> */
public class FileAttributes {
  public final Option<String> user;

  public final Option<String> group;

  public final Option<UnixFileMode> mode;

  public final List<String> tags;

  public static final Show<FileAttributes> singleLineShow = Show.anyShow();

  /** A file object with all none fields. Use this when creating template objects. */
  public static final FileAttributes EMPTY =
      new FileAttributes(
          Option.<String>none(),
          Option.<String>none(),
          Option.<UnixFileMode>none(),
          List.<String>nil());

  public FileAttributes(Option<String> user, Option<String> group, Option<UnixFileMode> mode) {
    this(user, group, mode, List.<String>nil());
  }

  public FileAttributes(
      Option<String> user, Option<String> group, Option<UnixFileMode> mode, List<String> tags) {
    validateNotNull(user, group, mode, tags);
    this.user = user;
    this.group = group;
    this.mode = mode;
    this.tags = tags;
  }

  public FileAttributes user(String user) {
    return new FileAttributes(fromNull(user), group, mode, tags);
  }

  public FileAttributes user(Option<String> user) {
    return new FileAttributes(user, group, mode, tags);
  }

  public FileAttributes group(String group) {
    return new FileAttributes(user, fromNull(group), mode, tags);
  }

  public FileAttributes group(Option<String> group) {
    return new FileAttributes(user, group, mode, tags);
  }

  public FileAttributes mode(UnixFileMode mode) {
    return new FileAttributes(user, group, fromNull(mode), tags);
  }

  public FileAttributes mode(Option<UnixFileMode> mode) {
    return new FileAttributes(user, group, mode, tags);
  }

  public FileAttributes addTag(String tag) {
    return new FileAttributes(user, group, mode, tags.append(single(tag)));
  }

  public FileAttributes tags(List<String> tags) {
    return new FileAttributes(user, group, mode, this.tags.append(tags));
  }

  // -----------------------------------------------------------------------
  //
  // -----------------------------------------------------------------------

  public FileAttributes useAsDefaultsFor(FileAttributes other) {
    return new FileAttributes(
        other.user.orElse(user), other.group.orElse(group), other.mode.orElse(mode), other.tags);
  }

  // -----------------------------------------------------------------------
  //
  // -----------------------------------------------------------------------

  public static final F2<FileAttributes, FileAttributes, FileAttributes> useAsDefaultsFor =
      new F2<FileAttributes, FileAttributes, FileAttributes>() {
        public FileAttributes f(FileAttributes defaults, FileAttributes other) {
          return defaults.useAsDefaultsFor(other);
        }
      };

  public static final F<FileAttributes, Option<String>> userF =
      new F<FileAttributes, Option<String>>() {
        public Option<String> f(FileAttributes attributes) {
          return attributes.user;
        }
      };

  public static final F<FileAttributes, Option<String>> groupF =
      new F<FileAttributes, Option<String>>() {
        public Option<String> f(FileAttributes attributes) {
          return attributes.group;
        }
      };

  public static final F<FileAttributes, Option<UnixFileMode>> modeF =
      new F<FileAttributes, Option<UnixFileMode>>() {
        public Option<UnixFileMode> f(FileAttributes attributes) {
          return attributes.mode;
        }
      };

  public static final F<FileAttributes, List<String>> tagsF =
      new F<FileAttributes, List<String>>() {
        public List<String> f(FileAttributes attributes) {
          return attributes.tags;
        }
      };

  // -----------------------------------------------------------------------
  // Object Overrides
  // -----------------------------------------------------------------------

  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }

    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    FileAttributes that = (FileAttributes) o;

    return optionEquals(user, that.user)
        && optionEquals(group, that.group)
        && optionEquals(mode, that.mode);
  }

  public String toString() {
    return "user="******"<not set>")
        + ", "
        + "group="
        + group.orSome("<not set>")
        + ", "
        + "mode="
        + mode.map(showLong).orSome("<not set>");
  }
}