/* Copyright (c) 2011 Danish Maritime Authority.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.maritimecloud.internal.msdl.parser;
import static java.util.Objects.requireNonNull;
import java.io.IOException;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import net.maritimecloud.internal.msdl.parser.ParsedMsdlFile.Import;
import net.maritimecloud.msdl.MsdlLogger;
import net.maritimecloud.msdl.MsdlPluginException;
/**
*
* @author Kasper Nielsen
*/
class ImportResolver implements Iterable<ParsedMsdlFile> {
/** A map of all msdl files. */
Map<String, Path> dependencies;
/** A map of all parsed msdl files. */
Map<String, ParsedMsdlFile> resolvedDependency = new LinkedHashMap<>();
final List<Path> directories;
final MsdlLogger logger;
/**
* @param directories
*/
ImportResolver(List<Path> directories, MsdlLogger logger) {
this.directories = requireNonNull(directories);
this.logger = requireNonNull(logger);
}
void addResolvedFile(String name, ParsedMsdlFile f, boolean isDependency) {
if (isDependency) {
logger.debug("Adding resolved file " + name + " -> " + f.antlrFile.getPath() + " as dependency");
}
name = name.replace('\\', '/'); // handling windows paths
resolvedDependency.put(name, f);
}
ParsedMsdlFile resolveImport(ParsedProject project, ParsedMsdlFile file, Import imp) throws IOException {
logger.debug("Trying to resolve import: " + imp.importContext.getChild(1).getText());
String name = imp.getName();
// lets see if we already resolved it
ParsedMsdlFile f = resolvedDependency.get(name);
if (f != null) {
return f;
}
// make sure we have found all dependencies
if (dependencies == null) {
dependencies = new LinkedHashMap<>();
for (final Path root : directories) {
logger.debug("Looking for .msdl dependencies in " + root);
SimpleFileVisitor<Path> sfv = new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.getFileName().toString().endsWith(".msdl")) {
String p = root.relativize(file).toString();
if (dependencies.containsKey(p)) {
logger.warn("Multiple files named '" + p + " existed, ignore second file, first = "
+ dependencies.get(p) + ", second = " + file);
} else {
logger.debug("Adding " + p + " -> " + file + " as dependency");
dependencies.put(p, file);
}
}
return super.visitFile(file, attrs);
}
};
try {
Files.walkFileTree(root, sfv);
} catch (IOException e) {
logger.error("Failed to process dependencies", e);
throw new MsdlPluginException("Failed to process dependencies", e);
}
}
}
Path p = dependencies.get(name);
if (p == null) {
// add-> "Looked in ...." list of directories
Enumeration<URL> resources = ImportResolver.class.getClassLoader().getResources(name);
List<URL> l = Collections.list(resources);
if (l.isEmpty()) {
file.error(imp.importContext, "Could not find import '" + name + "'");
} else {
f = project.parseFile(l.get(0));
if (l.size() > 1) {
logger.warn("Multiple files named '" + name + "' existed on the classpath, will use first file: "
+ l.get(0) + " All = " + l);
}
}
} else {
f = project.parseFile(p);
}
if (f != null) {
addResolvedFile(name, f, true);
}
return f;
}
void resolveAll(ParsedProject project, Collection<ParsedMsdlFile> initial) throws IOException {
LinkedHashSet<ParsedMsdlFile> allFiles = new LinkedHashSet<>(initial);
LinkedList<ParsedMsdlFile> unImportedFiles = new LinkedList<>(initial);
while (!unImportedFiles.isEmpty()) {
ParsedMsdlFile pf = unImportedFiles.poll();
for (Import importLocation : pf.imports) {
ParsedMsdlFile imp = resolveImport(project, pf, importLocation);
if (imp != null) {
if (!allFiles.contains(imp)) {
allFiles.add(imp);
unImportedFiles.add(imp);
}
pf.resolvedImports.add(imp);
}
}
}
}
/** {@inheritDoc} */
@Override
public Iterator<ParsedMsdlFile> iterator() {
return resolvedDependency.values().iterator();
}
}