*/
package org.opendaylight.yangtools.yang.model.repo.util;
-import com.google.common.base.Objects;
+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 java.io.File;
import java.io.FileInputStream;
+import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitResult;
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.text.SimpleDateFormat;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
-import org.opendaylight.yangtools.yang.model.util.repo.FilesystemSchemaCachingProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger LOG = LoggerFactory.getLogger(FilesystemSchemaSourceCache.class);
// Init storage adapters
- private static final Map<Class<? extends SchemaSourceRepresentation>, StorageAdapter<? extends SchemaSourceRepresentation>> storageAdapters =
- Collections.<Class<? extends SchemaSourceRepresentation>, StorageAdapter<? extends SchemaSourceRepresentation>> 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 + "))?");
+
private final Class<T> representation;
private final File storageDirectory;
checkSupportedRepresentation(representation);
- if(storageDirectory.exists() == false) {
+ if(!storageDirectory.exists()) {
Preconditions.checkArgument(storageDirectory.mkdirs(), "Unable to create cache directory at %s", storageDirectory);
}
Preconditions.checkArgument(storageDirectory.exists());
}
private static void checkSupportedRepresentation(final Class<? extends SchemaSourceRepresentation> representation) {
- for (final Class<? extends SchemaSourceRepresentation> supportedRepresentation : storageAdapters.keySet()) {
+ for (final Class<? extends SchemaSourceRepresentation> supportedRepresentation : STORAGE_ADAPTERS.keySet()) {
if(supportedRepresentation.isAssignableFrom(representation)) {
return;
}
}
throw new IllegalArgumentException(String.format(
- "This cache does not support representation: %s, supported representations are: %s", representation, storageAdapters.keySet()));
+ "This cache does not support representation: %s, supported representations are: %s", representation, STORAGE_ADAPTERS.keySet()));
}
- private static final Pattern CACHED_FILE_PATTERN =
- Pattern.compile(
- "(?<moduleName>[^@]+)" +
- "(@(?<revision>" + FilesystemSchemaCachingProvider.REVISION_PATTERN + "))?");
-
/**
* Restore cache state
*/
@Override
public synchronized CheckedFuture<? extends T, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
- final File file = FilesystemSchemaCachingProvider.sourceIdToFile(toLegacy(sourceIdentifier), storageDirectory);
+ final File file = sourceIdToFile(sourceIdentifier, storageDirectory);
if(file.exists() && file.canRead()) {
LOG.trace("Source {} found in cache as {}", sourceIdentifier, file);
- final SchemaSourceRepresentation restored = storageAdapters.get(representation).restore(sourceIdentifier, file);
+ final SchemaSourceRepresentation restored = STORAGE_ADAPTERS.get(representation).restore(sourceIdentifier, file);
return Futures.immediateCheckedFuture(representation.cast(restored));
}
LOG.debug("Source {} not found in cache as {}", sourceIdentifier, file);
- return Futures.<T, SchemaSourceException>immediateFailedCheckedFuture(new MissingSchemaSourceException("Source not found", sourceIdentifier));
+ return Futures.immediateFailedCheckedFuture(new MissingSchemaSourceException("Source not found", sourceIdentifier));
}
@Override
}
private File sourceIdToFile(final T source) {
- return FilesystemSchemaCachingProvider.sourceIdToFile(toLegacy(source.getIdentifier()), storageDirectory);
+ return sourceIdToFile(source.getIdentifier(), storageDirectory);
}
- private void storeSource(final File file, final T schemaRepresentation) {
- storageAdapters.get(representation).store(file, schemaRepresentation);
+ static File sourceIdToFile(final SourceIdentifier identifier, final File storageDirectory) {
+ final String rev = identifier.getRevision();
+ final File file;
+ if (Strings.isNullOrEmpty(rev)) {
+ file = findFileWithNewestRev(identifier, storageDirectory);
+ } else {
+ file = new File(storageDirectory, identifier.toYangFilename());
+ }
+ return file;
}
- private static org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier toLegacy(final SourceIdentifier identifier) {
- return new org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier(identifier.getName(), Optional.fromNullable(identifier.getRevision()));
+ 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)");
+
+ @Override
+ public boolean accept(final File dir, final String name) {
+ return p.matcher(name).matches();
+ }
+ });
+
+ if (files.length == 0) {
+ return new File(storageDirectory, identifier.toYangFilename());
+ }
+ if (files.length == 1) {
+ return files[0];
+ }
+
+ File file = null;
+ TreeMap<Date, File> map = new TreeMap<>();
+ 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 = new SimpleDateFormat("yyyy-MM-dd");
+ 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);
+ }
+
+ } else {
+ map.put(new Date(0L), sorted);
+ }
+ }
+ file = map.lastEntry().getValue();
+
+ return file;
+ }
+
+ private void storeSource(final File file, final T schemaRepresentation) {
+ STORAGE_ADAPTERS.get(representation).store(file, schemaRepresentation);
}
private static abstract class StorageAdapter<T extends SchemaSourceRepresentation> {
protected abstract void storeAsType(final File file, final T cast);
- public Class<T> getSupportedType() {
- return supportedType;
- }
-
public T restore(final SourceIdentifier sourceIdentifier, final File cachedSource) {
Preconditions.checkArgument(cachedSource.isFile());
Preconditions.checkArgument(cachedSource.exists());
@Override
protected void storeAsType(final File file, final YangTextSchemaSource cast) {
- try {
- Files.copy(cast.openStream(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ try (final 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);
}
return new YangTextSchemaSource(sourceIdentifier) {
@Override
- protected Objects.ToStringHelper addToStringAttributes(final Objects.ToStringHelper toStringHelper) {
+ protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) {
return toStringHelper;
}
return fileVisitResult;
}
- private Optional<SourceIdentifier> getSourceIdentifier(final String fileName) {
+ private static Optional<SourceIdentifier> getSourceIdentifier(final String fileName) {
final Matcher matcher = CACHED_FILE_PATTERN.matcher(fileName);
- if(matcher.matches()) {
+ if (matcher.matches()) {
final String moduleName = matcher.group("moduleName");
final String revision = matcher.group("revision");
return Optional.of(new SourceIdentifier(moduleName, Optional.fromNullable(revision)));