8bd3f194dbb6b17103e7504565673b824e3386e8
[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.YangTextSource;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 @NonNullByDefault
37 abstract class ScannedDependency {
38     private static final class Single extends ScannedDependency {
39         Single(final File file) {
40             super(file);
41         }
42
43         @Override
44         ImmutableList<YangTextSource> sources() {
45             return ImmutableList.of(YangTextSource.forPath(file().toPath()));
46         }
47     }
48
49     private static final class Zip extends ScannedDependency {
50         private final ImmutableSet<String> entryNames;
51
52         Zip(final File file, final ImmutableSet<String> entryNames) {
53             super(file);
54             this.entryNames = requireNonNull(entryNames);
55         }
56
57         @Override
58         ImmutableList<YangTextSource> sources() throws IOException {
59             final var builder = ImmutableList.<YangTextSource>builderWithExpectedSize(entryNames.size());
60
61             try (var zip = new ZipFile(file())) {
62                 for (var entryName : entryNames) {
63                     final var entry = requireNonNull(zip.getEntry(entryName));
64
65                     builder.add(new DelegatedYangTextSource(
66                         SourceIdentifier.ofYangFileName(entryName.substring(entryName.lastIndexOf('/') + 1)),
67                         ByteSource.wrap(zip.getInputStream(entry).readAllBytes())
68                             .asCharSource(StandardCharsets.UTF_8)));
69                 }
70             }
71
72             return builder.build();
73         }
74     }
75
76     private static final Logger LOG = LoggerFactory.getLogger(ScannedDependency.class);
77
78     private final File file;
79
80     ScannedDependency(final File file) {
81         this.file = requireNonNull(file);
82     }
83
84     static List<ScannedDependency> scanDependencies(final MavenProject project) throws IOException {
85         final List<File> filesOnCp = getClassPath(project);
86         LOG.debug("{} Searching for YANG files in dependencies: {}", YangToSourcesProcessor.LOG_PREFIX, filesOnCp);
87         LOG.debug("{} Searching for YANG files in {} dependencies", YangToSourcesProcessor.LOG_PREFIX,
88             filesOnCp.size());
89
90         final var result = new ArrayList<ScannedDependency>();
91         for (File file : filesOnCp) {
92             // is it jar file or directory?
93             if (file.isDirectory()) {
94                 final File yangDir = new File(file, META_INF_YANG_STRING);
95                 if (yangDir.exists() && yangDir.isDirectory()) {
96                     result.addAll(scanDirectory(yangDir));
97                 }
98             } else {
99                 result.addAll(scanZipFile(file));
100             }
101         }
102         return result;
103     }
104
105     private static ImmutableList<ScannedDependency> scanDirectory(final File yangDir) {
106         return Arrays.stream(yangDir.listFiles(
107             (dir, name) -> name.endsWith(RFC6020_YANG_FILE_EXTENSION) && new File(dir, name).isFile()))
108                 .map(Single::new)
109                 .collect(ImmutableList.toImmutableList());
110     }
111
112     private static ImmutableList<ScannedDependency> scanZipFile(final File zipFile) throws IOException {
113         final ImmutableSet<String> entryNames;
114         try (ZipFile zip = new ZipFile(zipFile)) {
115             entryNames = zip.stream()
116                 .filter(entry -> {
117                     final String entryName = entry.getName();
118                     return entryName.startsWith(META_INF_YANG_STRING_JAR) && !entry.isDirectory()
119                         && entryName.endsWith(RFC6020_YANG_FILE_EXTENSION);
120                 })
121                 .map(ZipEntry::getName)
122                 .collect(ImmutableSet.toImmutableSet());
123         }
124
125         return entryNames.isEmpty() ? ImmutableList.of() : ImmutableList.of(new Zip(zipFile, entryNames));
126     }
127
128     // FIXME: java.nio.file.Path
129     final File file() {
130         return file;
131     }
132
133     abstract ImmutableList<YangTextSource> sources() throws IOException;
134
135     @VisibleForTesting
136     static List<File> getClassPath(final MavenProject project) {
137         return project.getArtifacts().stream()
138             .map(Artifact::getFile)
139             .filter(file -> file.isFile() && file.getName().endsWith(".jar") || file.isDirectory())
140             .toList();
141     }
142 }