Move model.repo.util classes to model.repo.spi
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / repo / util / FilesystemSchemaSourceCache.java
index 0eb7fc00138c7f556d0d8b0cd8c0bc6fab9f2095..a62eb5342fb563c7f7b84087b0915812f9ba9bd8 100644 (file)
@@ -7,13 +7,14 @@
  */
 package org.opendaylight.yangtools.yang.model.repo.util;
 
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.Futures;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.util.concurrent.FluentFuture;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.io.File;
 import java.io.FilenameFilter;
 import java.io.IOException;
@@ -24,43 +25,42 @@ import java.nio.file.Path;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.attribute.BasicFileAttributes;
-import java.text.DateFormat;
-import java.text.ParseException;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.TreeMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.AbstractSchemaSourceCache;
 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Cache implementation that stores schemas in form of files under provided folder
+ * Cache implementation that stores schemas in form of files under provided folder.
  */
-public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentation> extends AbstractSchemaSourceCache<T> {
+public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentation>
+        extends AbstractSchemaSourceCache<T> {
 
     private static final Logger LOG = LoggerFactory.getLogger(FilesystemSchemaSourceCache.class);
 
     // Init storage adapters
-    private static final Map<Class<? extends SchemaSourceRepresentation>, StorageAdapter<? extends SchemaSourceRepresentation>> STORAGE_ADAPTERS =
-            Collections.singletonMap(
+    private static final Map<Class<? extends SchemaSourceRepresentation>,
+            StorageAdapter<? extends SchemaSourceRepresentation>> STORAGE_ADAPTERS = Collections.singletonMap(
                     YangTextSchemaSource.class, new YangTextSchemaStorageAdapter());
 
     private static final Pattern CACHED_FILE_PATTERN =
-            Pattern.compile(
-                    "(?<moduleName>[^@]+)" +
-                    "(@(?<revision>" + SourceIdentifier.REVISION_PATTERN + "))?");
+            Pattern.compile("(?<moduleName>[^@]+)" + "(@(?<revision>" + Revision.STRING_FORMAT_PATTERN + "))?");
 
     private final Class<T> representation;
     private final File storageDirectory;
@@ -69,17 +69,14 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
             final SchemaSourceRegistry consumer, final Class<T> representation, final File storageDirectory) {
         super(consumer, representation, Costs.LOCAL_IO);
         this.representation = representation;
-        this.storageDirectory = Preconditions.checkNotNull(storageDirectory);
+        this.storageDirectory = requireNonNull(storageDirectory);
 
         checkSupportedRepresentation(representation);
 
-        if (!storageDirectory.exists()) {
-            Preconditions.checkArgument(storageDirectory.mkdirs(), "Unable to create cache directory at %s", storageDirectory);
-        }
-        Preconditions.checkArgument(storageDirectory.exists());
-        Preconditions.checkArgument(storageDirectory.isDirectory());
-        Preconditions.checkArgument(storageDirectory.canWrite());
-        Preconditions.checkArgument(storageDirectory.canRead());
+        checkArgument(storageDirectory.mkdirs() || storageDirectory.isDirectory(),
+                "Unable to create cache directory at %s", storageDirectory);
+        checkArgument(storageDirectory.canWrite());
+        checkArgument(storageDirectory.canRead());
 
         init();
     }
@@ -91,12 +88,13 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
             }
         }
 
-       throw new IllegalArgumentException(String.format(
-                "This cache does not support representation: %s, supported representations are: %s", representation, STORAGE_ADAPTERS.keySet()));
+        throw new IllegalArgumentException(String.format(
+                   "This cache does not support representation: %s, supported representations are: %s",
+                   representation, STORAGE_ADAPTERS.keySet()));
     }
 
     /**
-     * Restore cache state
+     * Restore cache state.
      */
     private void init() {
 
@@ -104,26 +102,25 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
         try {
             Files.walkFileTree(storageDirectory.toPath(), fileVisitor);
         } catch (final IOException e) {
-            LOG.warn("Unable to restore cache from {}. Starting with empty cache", storageDirectory);
+            LOG.warn("Unable to restore cache from {}. Starting with an empty cache", storageDirectory, e);
             return;
         }
 
-        for (final SourceIdentifier cachedSchema : fileVisitor.getCachedSchemas()) {
-            register(cachedSchema);
-        }
+        fileVisitor.getCachedSchemas().stream().forEach(this::register);
     }
 
     @Override
-    public synchronized CheckedFuture<? extends T, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
+    public synchronized FluentFuture<? extends T> getSource(final SourceIdentifier sourceIdentifier) {
         final File file = sourceIdToFile(sourceIdentifier, storageDirectory);
         if (file.exists() && file.canRead()) {
             LOG.trace("Source {} found in cache as {}", sourceIdentifier, file);
-            final SchemaSourceRepresentation restored = STORAGE_ADAPTERS.get(representation).restore(sourceIdentifier, file);
-            return Futures.immediateCheckedFuture(representation.cast(restored));
+            final SchemaSourceRepresentation restored = STORAGE_ADAPTERS.get(representation).restore(sourceIdentifier,
+                    file);
+            return immediateFluentFuture(representation.cast(restored));
         }
 
         LOG.debug("Source {} not found in cache as {}", sourceIdentifier, file);
-        return Futures.immediateFailedCheckedFuture(new MissingSchemaSourceException("Source not found", sourceIdentifier));
+        return immediateFailedFluentFuture(new MissingSchemaSourceException("Source not found", sourceIdentifier));
     }
 
     @Override
@@ -145,9 +142,10 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
     }
 
     static File sourceIdToFile(final SourceIdentifier identifier, final File storageDirectory) {
-        final String rev = identifier.getRevision();
+        final Optional<Revision> rev = identifier.getRevision();
         final File file;
-        if (Strings.isNullOrEmpty(rev)) {
+        if (rev.isEmpty()) {
+            // FIXME: this does not look right
             file = findFileWithNewestRev(identifier, storageDirectory);
         } else {
             file = new File(storageDirectory, identifier.toYangFilename());
@@ -155,13 +153,16 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
         return file;
     }
 
+    @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE",
+            justification = "listFiles is analyzed to return null")
     private static File findFileWithNewestRev(final SourceIdentifier identifier, final File storageDirectory) {
         File[] files = storageDirectory.listFiles(new FilenameFilter() {
-            final Pattern p = Pattern.compile(Pattern.quote(identifier.getName()) + "(\\.yang|@\\d\\d\\d\\d-\\d\\d-\\d\\d.yang)");
+            final Pattern pat = Pattern.compile(Pattern.quote(identifier.getName())
+                    + "(\\.yang|@\\d\\d\\d\\d-\\d\\d-\\d\\d.yang)");
 
             @Override
             public boolean accept(final File dir, final String name) {
-                return p.matcher(name).matches();
+                return pat.matcher(name).matches();
             }
         });
 
@@ -173,28 +174,24 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
         }
 
         File file = null;
-        TreeMap<Date, File> map = new TreeMap<>();
+        TreeMap<Optional<Revision>, File> map = new TreeMap<>(Revision::compare);
         for (File sorted : files) {
             String fileName = sorted.getName();
-            Matcher m = SourceIdentifier.REVISION_PATTERN.matcher(fileName);
-            if (m.find()) {
-                String revStr = m.group();
-                /*
-                 * FIXME: Consider using string for comparison.
-                 * String is comparable, pattern check tested format
-                 * so comparing as ASCII string should be sufficient
-                 */
-                DateFormat df = SimpleDateFormatUtil.getRevisionFormat();
+            Matcher match = Revision.STRING_FORMAT_PATTERN.matcher(fileName);
+            if (match.find()) {
+                String revStr = match.group();
+                Revision rev;
                 try {
-                    Date d = df.parse(revStr);
-                    map.put(d, sorted);
-                } catch (final ParseException e) {
-                    LOG.info("Unable to parse date from yang file name {}", fileName);
-                    map.put(new Date(0L), sorted);
+                    rev = Revision.of(revStr);
+                } catch (final DateTimeParseException e) {
+                    LOG.info("Unable to parse date from yang file name {}, falling back to not-present", fileName, e);
+                    rev = null;
                 }
 
+                map.put(Optional.ofNullable(rev), sorted);
+
             } else {
-                map.put(new Date(0L), sorted);
+                map.put(Optional.empty(), sorted);
             }
         }
         file = map.lastEntry().getValue();
@@ -206,7 +203,7 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
         STORAGE_ADAPTERS.get(representation).store(file, schemaRepresentation);
     }
 
-    private static abstract class StorageAdapter<T extends SchemaSourceRepresentation> {
+    private abstract static class StorageAdapter<T extends SchemaSourceRepresentation> {
 
         private final Class<T> supportedType;
 
@@ -215,23 +212,23 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
         }
 
         void store(final File file, final SchemaSourceRepresentation schemaSourceRepresentation) {
-            Preconditions.checkArgument(supportedType.isAssignableFrom(schemaSourceRepresentation.getClass()),
-                    "Cannot store schema source %s, this adapter only supports %s", schemaSourceRepresentation, supportedType);
+            checkArgument(supportedType.isAssignableFrom(schemaSourceRepresentation.getClass()),
+                    "Cannot store schema source %s, this adapter only supports %s", schemaSourceRepresentation,
+                    supportedType);
 
             storeAsType(file, supportedType.cast(schemaSourceRepresentation));
-
         }
 
-        protected abstract void storeAsType(final File file, final T cast);
+        protected abstract void storeAsType(File file, T cast);
 
         public T restore(final SourceIdentifier sourceIdentifier, final File cachedSource) {
-            Preconditions.checkArgument(cachedSource.isFile());
-            Preconditions.checkArgument(cachedSource.exists());
-            Preconditions.checkArgument(cachedSource.canRead());
+            checkArgument(cachedSource.isFile());
+            checkArgument(cachedSource.exists());
+            checkArgument(cachedSource.canRead());
             return restoreAsType(sourceIdentifier, cachedSource);
         }
 
-        protected abstract T restoreAsType(final SourceIdentifier sourceIdentifier, final File cachedSource);
+        protected abstract T restoreAsType(SourceIdentifier sourceIdentifier, File cachedSource);
     }
 
     private static final class YangTextSchemaStorageAdapter extends StorageAdapter<YangTextSchemaSource> {
@@ -241,11 +238,14 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
         }
 
         @Override
+        @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE",
+            justification = "https://github.com/spotbugs/spotbugs/issues/600")
         protected void storeAsType(final File file, final YangTextSchemaSource cast) {
-            try (final InputStream castStream = cast.openStream()) {
+            try (InputStream castStream = cast.openStream()) {
                 Files.copy(castStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
             } catch (final IOException e) {
-                throw new IllegalStateException("Cannot store schema source " + cast.getIdentifier() + " to " + file, e);
+                throw new IllegalStateException("Cannot store schema source " + cast.getIdentifier() + " to " + file,
+                        e);
             }
         }
 
@@ -254,7 +254,7 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
             return new YangTextSchemaSource(sourceIdentifier) {
 
                 @Override
-                protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) {
+                protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
                     return toStringHelper;
                 }
 
@@ -267,7 +267,7 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
     }
 
     private static final class CachedModulesFileVisitor extends SimpleFileVisitor<Path> {
-        private final List<SourceIdentifier> cachedSchemas = Lists.newArrayList();
+        private final List<SourceIdentifier> cachedSchemas = new ArrayList<>();
 
         @Override
         public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
@@ -280,7 +280,8 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
                 LOG.trace("Restoring cached file {} as {}", file, si.get());
                 cachedSchemas.add(si.get());
             } else {
-                LOG.debug("Skipping cached file {}, cannot restore source identifier from filename: {}, does not match {}", file, fileName, CACHED_FILE_PATTERN);
+                LOG.debug("Skipping cached file {}, cannot restore source identifier from filename: {},"
+                        + " does not match {}", file, fileName, CACHED_FILE_PATTERN);
             }
             return fileVisitResult;
         }
@@ -290,9 +291,9 @@ public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentat
             if (matcher.matches()) {
                 final String moduleName = matcher.group("moduleName");
                 final String revision = matcher.group("revision");
-                return Optional.of(RevisionSourceIdentifier.create(moduleName, Optional.fromNullable(revision)));
+                return Optional.of(RevisionSourceIdentifier.create(moduleName, Revision.ofNullable(revision)));
             }
-            return Optional.absent();
+            return Optional.empty();
         }
 
         @Override