Rehost SourceIdentifier methods 56/109656/1
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 7 Jan 2024 12:46:51 +0000 (13:46 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 7 Jan 2024 13:01:02 +0000 (14:01 +0100)
SourceIdentifier is aware of .yang, expand its capabilities to also
parse file names to extract revision and handle .yin.

JIRA: YANGTOOLS-XXX
Change-Id: I5c41707677a9b8b75d81987f740a48162e2c8689
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
model/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/source/SourceIdentifier.java
model/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/source/YangTextSource.java
model/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/source/YinTextSource.java
parser/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/YangTextSchemaContextResolver.java
parser/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/StmtTestUtils.java

index fbb674267b0685e6d256317a164c21ed31b97bab..7efa97f49850b7124b220e7e287c33dac7cdde2c 100644 (file)
@@ -8,6 +8,8 @@
 package org.opendaylight.yangtools.yang.model.api.source;
 
 import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YANG_FILE_EXTENSION;
+import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YIN_FILE_EXTENSION;
 
 import java.time.format.DateTimeParseException;
 import org.eclipse.jdt.annotation.NonNull;
@@ -15,7 +17,9 @@ import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.concepts.Identifier;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.common.UnresolvedQName.Unqualified;
-import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.common.YangNames;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Base class of YANG Schema source identifiers. Source identifiers are designated to be carry only necessary
@@ -29,12 +33,14 @@ public record SourceIdentifier(@NonNull Unqualified name, @Nullable Revision rev
     @java.io.Serial
     private static final long serialVersionUID = 3L;
 
+    private static final Logger LOG = LoggerFactory.getLogger(SourceIdentifier.class);
+
     /**
      * Creates new YANG Schema source identifier for sources with or without a revision.
      *
      * @param name Name of schema
      * @param revision Revision of schema
-     * @throws NullPointerException if {@code name} is null
+     * @throws NullPointerException if {@code name} is {@code null}
      */
     public SourceIdentifier {
         requireNonNull(name);
@@ -44,7 +50,7 @@ public record SourceIdentifier(@NonNull Unqualified name, @Nullable Revision rev
      * Creates new YANG Schema source identifier for sources without a revision.
      *
      * @param name Name of schema
-     * @throws NullPointerException if {@code name} is null
+     * @throws NullPointerException if {@code name} is {@code null}
      */
     public SourceIdentifier(final @NonNull Unqualified name) {
         this(name, null);
@@ -54,7 +60,7 @@ public record SourceIdentifier(@NonNull Unqualified name, @Nullable Revision rev
      * Creates new YANG Schema source identifier for sources without a revision.
      *
      * @param name Name of schema
-     * @throws NullPointerException if {@code name} is null
+     * @throws NullPointerException if {@code name} is {@code null}
      * @throws IllegalArgumentException if {@code name} is not a valid YANG identifier
      */
     public SourceIdentifier(final @NonNull String name) {
@@ -66,19 +72,38 @@ public record SourceIdentifier(@NonNull Unqualified name, @Nullable Revision rev
      *
      * @param name Name of schema
      * @param revision Optional schema revision
-     * @throws NullPointerException if {@code name} is null
+     * @throws NullPointerException if {@code name} is {@code null}
      * @throws IllegalArgumentException if {@code name} is not a valid YANG identifier
      */
     public SourceIdentifier(final @NonNull String name, final @Nullable Revision revision) {
         this(Unqualified.of(name), revision);
     }
 
+    public static @NonNull SourceIdentifier ofYinFileName(final String fileName) {
+        if (fileName.endsWith(RFC6020_YIN_FILE_EXTENSION)) {
+            return ofFileName(fileName.substring(0, fileName.length() - RFC6020_YIN_FILE_EXTENSION.length()));
+        }
+        throw new IllegalArgumentException("Filename " + fileName + " does not end with '.yin'");
+    }
+
+    public static @NonNull SourceIdentifier ofYangFileName(final String fileName) {
+        if (fileName.endsWith(RFC6020_YANG_FILE_EXTENSION)) {
+            return ofFileName(fileName.substring(0, fileName.length() - RFC6020_YANG_FILE_EXTENSION.length()));
+        }
+        throw new IllegalArgumentException("Filename '" + fileName + "' does not end with '.yang'");
+    }
+
+    private static @NonNull SourceIdentifier ofFileName(final String fileName) {
+        final var parsed = YangNames.parseFilename(fileName);
+        return new SourceIdentifier(parsed.getKey(), parsed.getValue());
+    }
+
     /**
      * Creates new YANG Schema source identifier for sources with or without a revision.
      *
      * @param name Name of schema
      * @param revision Optional schema revision
-     * @throws NullPointerException if {@code name} is null
+     * @throws NullPointerException if {@code name} is {@code null}
      * @throws IllegalArgumentException if {@code name} is not a valid YANG identifier
      * @throws DateTimeParseException if {@code revision} format does not conform specification.
      */
@@ -87,13 +112,12 @@ public record SourceIdentifier(@NonNull Unqualified name, @Nullable Revision rev
     }
 
     /**
-     * Returns filename for this YANG module as specified in RFC 6020.
-     *
-     * <p>
-     * Returns filename in format <code>name ['@' revision] '.yang'</code>, where revision is date in format YYYY-mm-dd.
+     * Returns filename for this YANG module as specified in
+     * <a href="https://www.rfc-editor.org/rfc/rfc6020#section-5.2">RFC 6020</a>.
      *
      * <p>
-     * @see <a href="http://www.rfc-editor.org/rfc/rfc6020#section-5.2">RFC6020</a>
+     * Returns filename formatted as {@code moduleName ['@' revision] '.yang'}, where revision-date is in format
+     * {@code YYYY-mm-dd}.
      *
      * @return Filename for this source identifier.
      */
@@ -101,6 +125,20 @@ public record SourceIdentifier(@NonNull Unqualified name, @Nullable Revision rev
         return toYangFileName(name.getLocalName(), revision);
     }
 
+    /**
+     * Returns filename for this YANG module as specified in
+     * <a href="https://www.rfc-editor.org/rfc/rfc6020#section-5.2">RFC 6020</a>.
+     *
+     * <p>
+     * Returns filename formatted as {@code moduleName ['@' revision] '.yin'}, where revision-date is in format
+     * {@code YYYY-mm-dd}.
+     *
+     * @return Filename for this source identifier.
+     */
+    public @NonNull String toYinFilename() {
+        return toYinFileName(name.getLocalName(), revision);
+    }
+
     @Override
     public String toString() {
         final var sb = new StringBuilder("SourceIdentifier [").append(name.getLocalName());
@@ -111,24 +149,43 @@ public record SourceIdentifier(@NonNull Unqualified name, @Nullable Revision rev
     }
 
     /**
-     * Returns filename for this YANG module as specified in RFC 6020.
+     * Returns filename for this YANG module as specified in
+     * <a href="https://www.rfc-editor.org/rfc/rfc6020#section-5.2">RFC 6020</a>.
      *
      * <p>
-     * Returns filename in format <code>moduleName ['@' revision] '.yang'</code>,
-     * where Where revision-date is in format YYYY-mm-dd.
+     * Returns filename formatted as {@code moduleName ['@' revision] '.yang'}, where revision-date is in format
+     * {@code YYYY-mm-dd}.
+     *
+     * @param moduleName module name
+     * @param revision optional revision
+     * @return Filename for this source identifier.
+     */
+    public static @NonNull String toYangFileName(final @NonNull String moduleName, final @Nullable Revision revision) {
+        return toFileName(moduleName, revision, RFC6020_YANG_FILE_EXTENSION);
+    }
+
+    /**
+     * Returns filename for this YANG module as specified in
+     * <a href="https://www.rfc-editor.org/rfc/rfc6020#section-5.2">RFC 6020</a>.
      *
      * <p>
-     * See http://www.rfc-editor.org/rfc/rfc6020#section-5.2
+     * Returns filename formatted as {@code moduleName ['@' revision] '.yin'}, where Where revision-date is in format
+     * {@code YYYY-mm-dd}.
      *
      * @param moduleName module name
      * @param revision optional revision
      * @return Filename for this source identifier.
      */
-    public static @NonNull String toYangFileName(final @NonNull String moduleName, final @Nullable Revision revision) {
+    public static @NonNull String toYinFileName(final @NonNull String moduleName, final @Nullable Revision revision) {
+        return toFileName(moduleName, revision, RFC6020_YIN_FILE_EXTENSION);
+    }
+
+    private static @NonNull String toFileName(final @NonNull String moduleName, final @Nullable Revision revision,
+            final @NonNull String extension) {
         final var sb = new StringBuilder(moduleName);
         if (revision != null) {
             sb.append('@').append(revision);
         }
-        return sb.append(YangConstants.RFC6020_YANG_FILE_EXTENSION).toString();
+        return sb.append(extension).toString();
     }
 }
index 335120ed191dc4fe9727d19341429f51637d00ce..6c565eb90f6a71c60f756aa5afe43adcd7e441cc 100644 (file)
@@ -7,10 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.model.spi.source;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
-import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YANG_FILE_EXTENSION;
-import static org.opendaylight.yangtools.yang.common.YangNames.parseFilename;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.MoreObjects.ToStringHelper;
@@ -38,15 +35,6 @@ public abstract class YangTextSource extends CharSource implements YangSourceRep
         this.sourceId = requireNonNull(sourceId);
     }
 
-    public static @NonNull SourceIdentifier identifierFromFilename(final String name) {
-        checkArgument(name.endsWith(RFC6020_YANG_FILE_EXTENSION), "Filename '%s' does not end with '%s'", name,
-            RFC6020_YANG_FILE_EXTENSION);
-
-        final String baseName = name.substring(0, name.length() - RFC6020_YANG_FILE_EXTENSION.length());
-        final var parsed = parseFilename(baseName);
-        return new SourceIdentifier(parsed.getKey(), parsed.getValue());
-    }
-
     /**
      * Create a new YangTextSchemaSource with a specific source identifier and backed
      * by ByteSource, which provides the actual InputStreams.
@@ -99,7 +87,7 @@ public abstract class YangTextSource extends CharSource implements YangSourceRep
      */
     public static @NonNull YangTextSource delegateForCharSource(final String fileName,
             final CharSource delegate) {
-        return new DelegatedYangTextSource(identifierFromFilename(fileName), delegate);
+        return new DelegatedYangTextSource(SourceIdentifier.ofYangFileName(fileName), delegate);
     }
 
     /**
@@ -112,7 +100,8 @@ public abstract class YangTextSource extends CharSource implements YangSourceRep
      * @throws NullPointerException if file is {@code null}
      */
     public static @NonNull YangTextSource forPath(final Path path) {
-        return forPath(path, identifierFromFilename(path.toFile().getName()));
+        // FIXME: do not use .toFile() here
+        return forPath(path, SourceIdentifier.ofYangFileName(path.toFile().getName()));
     }
 
     /**
@@ -140,8 +129,10 @@ public abstract class YangTextSource extends CharSource implements YangSourceRep
      */
     public static @NonNull YangTextSource forPath(final Path path, final SourceIdentifier identifier,
             final Charset charset) {
-        checkArgument(Files.isRegularFile(path), "Supplied path %s is not a regular file", path);
-        return new YangTextFileSource(identifier, path, charset);
+        if (Files.isRegularFile(path)) {
+            return new YangTextFileSource(identifier, path, charset);
+        }
+        throw new IllegalArgumentException("Supplied path " + path + " is not a regular file");
     }
 
     /**
@@ -181,9 +172,9 @@ public abstract class YangTextSource extends CharSource implements YangSourceRep
      */
     public static @NonNull YangTextSource forResource(final Class<?> clazz, final String resourceName,
             final Charset charset) {
-        final String fileName = resourceName.substring(resourceName.lastIndexOf('/') + 1);
-        final SourceIdentifier identifier = identifierFromFilename(fileName);
-        final URL url = Resources.getResource(clazz, resourceName);
+        final var fileName = resourceName.substring(resourceName.lastIndexOf('/') + 1);
+        final var identifier = SourceIdentifier.ofYangFileName(fileName);
+        final var url = Resources.getResource(clazz, resourceName);
         return new ResourceYangTextSource(identifier, url, charset);
     }
 
index 1e780874731c0430cfec522176a10dbc751f274d..f84f1950a484b935ffd82817bfc221da0329482a 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.yangtools.yang.model.spi.source;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.MoreObjects;
@@ -18,42 +17,19 @@ import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.yang.common.YangConstants;
-import org.opendaylight.yangtools.yang.common.YangNames;
 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.source.YinSourceRepresentation;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * YIN text schema source representation. Exposes an RFC6020 or RFC7950 XML representation as an {@link InputStream}.
  */
 public abstract class YinTextSource extends ByteSource implements YinSourceRepresentation {
-    private static final Logger LOG = LoggerFactory.getLogger(YinTextSource.class);
-    private static final String XML_EXTENSION = ".xml";
-
     private final @NonNull SourceIdentifier sourceId;
 
     protected YinTextSource(final SourceIdentifier sourceId) {
         this.sourceId = requireNonNull(sourceId);
     }
 
-    public static @NonNull SourceIdentifier identifierFromFilename(final String name) {
-        final String baseName;
-        if (name.endsWith(YangConstants.RFC6020_YIN_FILE_EXTENSION)) {
-            baseName = name.substring(0, name.length() - YangConstants.RFC6020_YIN_FILE_EXTENSION.length());
-        } else if (name.endsWith(XML_EXTENSION)) {
-            // FIXME: BUG-7061: remove this once we do not need it
-            LOG.warn("XML file {} being loaded as YIN", name);
-            baseName = name.substring(0, name.length() - XML_EXTENSION.length());
-        } else {
-            throw new IllegalArgumentException("Filename " + name + " does not have a .yin or .xml extension");
-        }
-
-        final var parsed = YangNames.parseFilename(baseName);
-        return new SourceIdentifier(parsed.getKey(), parsed.getValue());
-    }
-
     @Override
     public final SourceIdentifier sourceId() {
         return sourceId;
@@ -96,13 +72,16 @@ public abstract class YinTextSource extends ByteSource implements YinSourceRepre
     }
 
     public static @NonNull YinTextSource forPath(final Path path) {
-        checkArgument(Files.isRegularFile(path), "Supplied path %s is not a regular file", path);
-        return new YinTextFileSource(identifierFromFilename(path.toFile().getName()), path);
+        if (Files.isRegularFile(path)) {
+            // FIXME: do not use toFile() here
+            return new YinTextFileSource(SourceIdentifier.ofYinFileName(path.toFile().getName()), path);
+        }
+        throw new IllegalArgumentException("Supplied path " + path + " is not a regular file");
     }
 
     public static @NonNull YinTextSource forResource(final Class<?> clazz, final String resourceName) {
         final String fileName = resourceName.substring(resourceName.lastIndexOf('/') + 1);
-        return new ResourceYinTextSource(identifierFromFilename(fileName),
+        return new ResourceYinTextSource(SourceIdentifier.ofYinFileName(fileName),
             Resources.getResource(clazz, resourceName));
     }
 }
index 5401feaf63973cabf1beb2e11d22702efecdbb48..6667210efffc32d929ef7690134e8738d333a87a 100644 (file)
@@ -233,7 +233,7 @@ public final class YangTextSchemaContextResolver implements AutoCloseable, Schem
 
     private static SourceIdentifier guessSourceIdentifier(final @NonNull String fileName) {
         try {
-            return YangTextSource.identifierFromFilename(fileName);
+            return SourceIdentifier.ofYangFileName(fileName);
         } catch (IllegalArgumentException e) {
             LOG.warn("Invalid file name format in '{}'", fileName, e);
             return new SourceIdentifier(fileName);
index fbb2a7f24f66284677407fa37435ac560f0dec75..e760938fcafd02bc4eb0d86b297ede44802e9614 100644 (file)
@@ -145,16 +145,16 @@ public final class StmtTestUtils {
     }
 
     public static EffectiveModelContext parseYinSources(final String yinSourcesDirectoryPath,
-            final YangParserConfiguration config) throws URISyntaxException, SAXException, IOException,
-            ReactorException {
-        final URL resourceDir = StmtTestUtils.class.getResource(yinSourcesDirectoryPath);
-        final File[] files = new File(resourceDir.toURI()).listFiles(YIN_FILE_FILTER);
-        final StatementStreamSource[] sources = new StatementStreamSource[files.length];
+            final YangParserConfiguration config)
+                throws URISyntaxException, SAXException, IOException, ReactorException {
+        final var resourceDir = StmtTestUtils.class.getResource(yinSourcesDirectoryPath);
+        final var files = new File(resourceDir.toURI()).listFiles(YIN_FILE_FILTER);
+        final var sources = new StatementStreamSource[files.length];
         for (int i = 0; i < files.length; i++) {
-            final SourceIdentifier identifier = YinTextSource.identifierFromFilename(files[i].getName());
-
+            final var file = files[i];
             sources[i] = YinStatementStreamSource.create(YinTextToDomTransformer.transformSource(
-                YinTextSource.delegateForByteSource(identifier, Files.asByteSource(files[i]))));
+                YinTextSource.delegateForByteSource(SourceIdentifier.ofYinFileName(file.getName()),
+                    Files.asByteSource(file))));
         }
 
         return parseYinSources(config, sources);