/*
 * Copyright 2013 Google 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.google.jimfs.attribute.providers;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.jimfs.attribute.AbstractAttributeProvider;
import com.google.jimfs.attribute.AttributeSpec;
import com.google.jimfs.attribute.AttributeStore;

import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.UserPrincipal;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Attribute provider that provides the "unix" attribute view.
 *
 * @author Colin Decker
 */
public class UnixAttributeProvider extends AbstractAttributeProvider {

  public static final String UID = "uid";
  public static final String INO = "ino";
  public static final String DEV = "dev";
  public static final String NLINK = "nlink";
  public static final String RDEV = "rdev";
  public static final String CTIME = "ctime";
  public static final String MODE = "mode";
  public static final String GID = "gid";

  private static final ImmutableSet<AttributeSpec> ATTRIBUTES = ImmutableSet.of(
      AttributeSpec.unsettable(UID, Integer.class),
      AttributeSpec.unsettable(INO, Long.class),
      AttributeSpec.unsettable(DEV, Long.class),
      AttributeSpec.unsettable(NLINK, Integer.class),
      AttributeSpec.unsettable(RDEV, Long.class),
      AttributeSpec.unsettable(CTIME, FileTime.class),
      AttributeSpec.unsettable(MODE, Integer.class),
      AttributeSpec.unsettable(GID, Integer.class));

  private final LoadingCache<Object, Integer> idCache = CacheBuilder.newBuilder()
      .build(new CacheLoader<Object, Integer>() {
        AtomicInteger uidGenerator = new AtomicInteger();

        @Override
        public Integer load(Object key) throws Exception {
          return uidGenerator.incrementAndGet();
        }
      });

  private final OwnerAttributeProvider owner;
  private final PosixAttributeProvider posix;

  public UnixAttributeProvider(PosixAttributeProvider posix) {
    super(ATTRIBUTES);
    this.owner = checkNotNull(posix.owner);
    this.posix = checkNotNull(posix);
  }

  @Override
  public String name() {
    return "unix";
  }

  @Override
  public ImmutableSet<String> inherits() {
    return ImmutableSet.of("basic", "owner", "posix");
  }

  @Override
  public void setInitial(AttributeStore store) {
    // doesn't actually set anything in the attribute map
  }

  @SuppressWarnings("unchecked")
  @Override
  public Object get(AttributeStore store, String attribute) {
    switch (attribute) {
      case UID:
        UserPrincipal user = (UserPrincipal) owner.get(store, OwnerAttributeProvider.OWNER);
        return idCache.getUnchecked(user);
      case GID:
        GroupPrincipal group = (GroupPrincipal) posix.get(store, PosixAttributeProvider.GROUP);
        return idCache.getUnchecked(group);
      case MODE:
        Set<PosixFilePermission> permissions
            = (Set<PosixFilePermission>) posix.get(store, PosixAttributeProvider.PERMISSIONS);
        return toMode(permissions);
      case CTIME:
        return BasicAttributeProvider.INSTANCE.get(store, BasicAttributeProvider.CREATION_TIME);
      case RDEV:
        return 0L;
      case DEV:
        return 1L;
      case INO:
        return idCache.getUnchecked(store);
      case NLINK:
        return store.links();
    }
    return super.get(store, attribute);
  }

  @SuppressWarnings("OctalInteger")
  private static int toMode(Set<PosixFilePermission> permissions) {
    int result = 0;
    for (PosixFilePermission permission : permissions) {
      switch (permission) {
        case OWNER_READ:
          result |= 0400;
          break;
        case OWNER_WRITE:
          result |= 0200;
          break;
        case OWNER_EXECUTE:
          result |= 0100;
          break;
        case GROUP_READ:
          result |= 0040;
          break;
        case GROUP_WRITE:
          result |= 0020;
          break;
        case GROUP_EXECUTE:
          result |= 0010;
          break;
        case OTHERS_READ:
          result |= 0004;
          break;
        case OTHERS_WRITE:
          result |= 0002;
          break;
        case OTHERS_EXECUTE:
          result |= 0001;
          break;
      }
    }
    return result;
  }
}

@@ -706,142 +706,8 @@
 l;%0A%0A
-import com.google.common.cache.CacheBuilder;%0Aimport com.google.common.cache.CacheLoader;%0Aimport com.google.common.cache.LoadingCache;%0A
 impo
@@ -745,24 +745,24 @@
 mutableSet;%0A
+
 import com.g
@@ -1114,24 +1114,114 @@
 a.util.Set;%0A
+import java.util.concurrent.ConcurrentHashMap;%0Aimport java.util.concurrent.ConcurrentMap;%0A
 import java.
@@ -2295,326 +2295,139 @@
 nal 
-LoadingCache%3CObject, Integer%3E idCache = CacheBuilder.newBuilder()%0A      .build(new CacheLoader%3CObject, Integer%3E() %7B%0A        AtomicInteger uidGenerator = new AtomicInteger();%0A%0A        @Override%0A        public Integer load(Object key) throws Exception %7B%0A          return uidGenerator.incrementAndGet();%0A        %7D%0A      %7D
+AtomicInteger uidGenerator = new AtomicInteger();%0A  private final ConcurrentMap%3CObject, Integer%3E idCache = new ConcurrentHashMap%3C%3E(
 );%0A%0A
@@ -2988,24 +2988,425 @@
 te map%0A  %7D%0A%0A
+  /**%0A   * Returns an ID that is guaranteed to be the same for any invocation with equal objects.%0A   */%0A  private Integer getUniqueId(Object object) %7B%0A    Integer id = idCache.get(object);%0A    if (id == null) %7B%0A      id = uidGenerator.incrementAndGet();%0A      Integer existing = idCache.putIfAbsent(object, id);%0A      if (existing != null) %7B%0A        return existing;%0A      %7D%0A    %7D%0A    return id;%0A  %7D%0A%0A
   @SuppressW
@@ -3649,35 +3649,26 @@
  return 
-idCache.getUnchecke
+getUniqueI
 d(user);
@@ -3795,35 +3795,26 @@
  return 
-idCache.getUnchecke
+getUniqueI
 d(group)
@@ -4229,27 +4229,18 @@
 urn 
-idCache.getUnchecke
+getUniqueI
 d(st

Replace use of LoadingCache, which is @Beta, with just a ConcurrentMap.