Expose FileYangTextSource
[yangtools.git] / plugin / yang-maven-plugin / src / main / java / org / opendaylight / yangtools / yang2sources / plugin / ScannedDependency.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang2sources.plugin;
9
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YANG_FILE_EXTENSION;
12 import static org.opendaylight.yangtools.yang2sources.plugin.YangToSourcesProcessor.META_INF_YANG_STRING;
13 import static org.opendaylight.yangtools.yang2sources.plugin.YangToSourcesProcessor.META_INF_YANG_STRING_JAR;
14
15 import com.google.common.annotations.VisibleForTesting;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.collect.ImmutableSet;
18 import com.google.common.io.ByteSource;
19 import java.io.File;
20 import java.io.IOException;
21 import java.nio.charset.StandardCharsets;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.List;
25 import java.util.zip.ZipEntry;
26 import java.util.zip.ZipFile;
27 import org.apache.maven.artifact.Artifact;
28 import org.apache.maven.project.MavenProject;
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
31 import org.opendaylight.yangtools.yang.model.spi.source.DelegatedYangTextSource;
32 import org.opendaylight.yangtools.yang.model.spi.source.FileYangTextSource;
33 import org.opendaylight.yangtools.yang.model.spi.source.YangTextSource;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 @NonNullByDefault
38 abstract class ScannedDependency {
39     private static final class Single extends ScannedDependency {
40         Single(final File file) {
41             super(file);
42         }
43
44         @Override
45         ImmutableList<YangTextSource> sources() {
46             return ImmutableList.of(new FileYangTextSource(file().toPath()));
47         }
48     }
49
50     private static final class Zip extends ScannedDependency {
51         private final ImmutableSet<String> entryNames;
52
53         Zip(final File file, final ImmutableSet<String> entryNames) {
54             super(file);
55             this.entryNames = requireNonNull(entryNames);
56         }
57
58         @Override
59         ImmutableList<YangTextSource> sources() throws IOException {
60             final var builder = ImmutableList.<YangTextSource>builderWithExpectedSize(entryNames.size());
61
62             try (var zip = new ZipFile(file())) {
63                 for (var entryName : entryNames) {
64                     final var entry = requireNonNull(zip.getEntry(entryName));
65
66                     builder.add(new DelegatedYangTextSource(
67                         SourceIdentifier.ofYangFileName(entryName.substring(entryName.lastIndexOf('/') + 1)),
68                         ByteSource.wrap(zip.getInputStream(entry).readAllBytes())
69                             .asCharSource(StandardCharsets.UTF_8)));
70                 }
71             }
72
73             return builder.build();
74         }
75     }
76
77     private static final Logger LOG = LoggerFactory.getLogger(ScannedDependency.class);
78
79     private final File file;
80
81     ScannedDependency(final File file) {
82         this.file = requireNonNull(file);
83     }
84
85     static List<ScannedDependency> scanDependencies(final MavenProject project) throws IOException {
86         final List<File> filesOnCp = getClassPath(project);
87         LOG.debug("{} Searching for YANG files in dependencies: {}", YangToSourcesProcessor.LOG_PREFIX, filesOnCp);
88         LOG.debug("{} Searching for YANG files in {} dependencies", YangToSourcesProcessor.LOG_PREFIX,
89             filesOnCp.size());
90
91         final var result = new ArrayList<ScannedDependency>();
92         for (File file : filesOnCp) {
93             // is it jar file or directory?
94             if (file.isDirectory()) {
95                 final File yangDir = new File(file, META_INF_YANG_STRING);
96                 if (yangDir.exists() && yangDir.isDirectory()) {
97                     result.addAll(scanDirectory(yangDir));
98                 }
99             } else {
100                 result.addAll(scanZipFile(file));
101             }
102         }
103         return result;
104     }
105
106     private static ImmutableList<ScannedDependency> scanDirectory(final File yangDir) {
107         return Arrays.stream(yangDir.listFiles(
108             (dir, name) -> name.endsWith(RFC6020_YANG_FILE_EXTENSION) && new File(dir, name).isFile()))
109                 .map(Single::new)
110                 .collect(ImmutableList.toImmutableList());
111     }
112
113     private static ImmutableList<ScannedDependency> scanZipFile(final File zipFile) throws IOException {
114         final ImmutableSet<String> entryNames;
115         try (ZipFile zip = new ZipFile(zipFile)) {
116             entryNames = zip.stream()
117                 .filter(entry -> {
118                     final String entryName = entry.getName();
119                     return entryName.startsWith(META_INF_YANG_STRING_JAR) && !entry.isDirectory()
120                         && entryName.endsWith(RFC6020_YANG_FILE_EXTENSION);
121                 })
122                 .map(ZipEntry::getName)
123                 .collect(ImmutableSet.toImmutableSet());
124         }
125
126         return entryNames.isEmpty() ? ImmutableList.of() : ImmutableList.of(new Zip(zipFile, entryNames));
127     }
128
129     // FIXME: java.nio.file.Path
130     final File file() {
131         return file;
132     }
133
134     abstract ImmutableList<YangTextSource> sources() throws IOException;
135
136     @VisibleForTesting
137     static List<File> getClassPath(final MavenProject project) {
138         return project.getArtifacts().stream()
139             .map(Artifact::getFile)
140             .filter(file -> file.isFile() && file.getName().endsWith(".jar") || file.isDirectory())
141             .toList();
142     }
143 }