1 /*
  2  *  Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
  3  *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  *  This code is free software; you can redistribute it and/or modify it
  6  *  under the terms of the GNU General Public License version 2 only, as
  7  *  published by the Free Software Foundation.  Oracle designates this
  8  *  particular file as subject to the "Classpath" exception as provided
  9  *  by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  *  This code is distributed in the hope that it will be useful, but WITHOUT
 12  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  *  version 2 for more details (a copy is included in the LICENSE file that
 15  *  accompanied this code).
 16  *
 17  *  You should have received a copy of the GNU General Public License version
 18  *  2 along with this work; if not, write to the Free Software Foundation,
 19  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  *   Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  *  or visit www.oracle.com if you need additional information or have any
 23  *  questions.
 24  *
 25  */
 26 
 27 package jdk.internal.jextract.impl;
 28 
 29 import javax.tools.JavaFileObject;
 30 import java.io.IOException;
 31 import java.io.InputStream;
 32 import java.nio.file.FileAlreadyExistsException;
 33 import java.nio.file.Files;
 34 import java.nio.file.Path;
 35 import java.util.List;
 36 import java.util.stream.Collectors;
 37 
 38 public final class Writer {
 39     private final List<? extends JavaFileObject> files;
 40     private final Path dest;
 41 
 42     public Writer(Path dest, List<? extends JavaFileObject> files) {
 43         this.files = files;
 44         this.dest = dest;
 45     }
 46 
 47     private List<JavaFileObject> ensureSourcesCompiled() {
 48         List<JavaFileObject> sources = sources();
 49         if (sources.isEmpty()) {
 50             return List.of();
 51         } else {
 52             return InMemoryJavaCompiler.compile(sources,
 53                 "--add-modules", "jdk.incubator.foreign",
 54                 "--release", "18",
 55                 "-d", dest.toAbsolutePath().toString(),
 56                 "-cp", dest.toAbsolutePath().toString());
 57         }
 58     }
 59 
 60     public void writeAll(boolean compileSources) throws IOException {
 61         writeClassFiles(resources());
 62         writeClassFiles(classes());
 63         if (compileSources) {
 64             writeClassFiles(ensureSourcesCompiled());
 65         } else {
 66             writeSourceFiles();
 67         }
 68     }
 69 
 70     void writeClassFiles(List<JavaFileObject> files) throws IOException {
 71         Path destDir = createOutputDir();
 72         for (var entry : files) {
 73             String path = entry.getName();
 74             Path fullPath = destDir.resolve(path).normalize();
 75             Files.createDirectories(fullPath.getParent());
 76             try (InputStream is = entry.openInputStream()) {
 77                 Files.write(fullPath, is.readAllBytes());
 78             }
 79         }
 80     }
 81 
 82     void writeSourceFiles() throws IOException {
 83         Path destDir = createOutputDir();
 84         for (var entry : sources()) {
 85             String srcPath = entry.getName();
 86             Path fullPath = destDir.resolve(srcPath).normalize();
 87             Path dir = fullPath.getParent();
 88             // In case the folder exist and is a link to a folder, this should be OK
 89             // Case in point, /tmp on MacOS link to /private/tmp
 90             if (Files.exists(dir)) {
 91                 if (!Files.isDirectory(dir)) {
 92                     throw new FileAlreadyExistsException(dir.toAbsolutePath().toString());
 93                 }
 94             } else {
 95                 Files.createDirectories(fullPath.getParent());
 96             }
 97             Files.write(fullPath, List.of(entry.getCharContent(false)));
 98         }
 99     }
100 
101     private List<JavaFileObject> sources() {
102         return files.stream()
103                 .filter(jfo -> jfo.getKind() == JavaFileObject.Kind.SOURCE)
104                 .collect(Collectors.toList());
105     }
106 
107     private List<JavaFileObject> classes() {
108         return files.stream()
109                 .filter(jfo -> jfo.getKind() == JavaFileObject.Kind.CLASS)
110                 .collect(Collectors.toList());
111     }
112 
113     private List<JavaFileObject> resources() {
114         return files.stream()
115                 .filter(jfo -> (jfo.getKind() == JavaFileObject.Kind.HTML || jfo.getKind() == JavaFileObject.Kind.OTHER))
116                 .collect(Collectors.toList());
117     }
118 
119     private Path createOutputDir() throws IOException {
120         Path absDest = dest.toAbsolutePath();
121         if (!Files.exists(absDest)) {
122             Files.createDirectories(absDest);
123         }
124         if (!Files.isDirectory(absDest)) {
125             throw new IOException("Not a directory: " + dest);
126         }
127         return absDest;
128     }
129 }