*/
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;
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;
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();
}
}
}
- 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() {
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
}
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());
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();
}
});
}
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();
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;
}
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> {
}
@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);
}
}
return new YangTextSchemaSource(sourceIdentifier) {
@Override
- protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) {
+ protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
return toStringHelper;
}
}
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 {
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;
}
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