1 /*
   2  *  Copyright (c) 2018, 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.
   8  *
   9  *  This code is distributed in the hope that it will be useful, but WITHOUT
  10  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  *  version 2 for more details (a copy is included in the LICENSE file that
  13  *  accompanied this code).
  14  *
  15  *  You should have received a copy of the GNU General Public License version
  16  *  2 along with this work; if not, write to the Free Software Foundation,
  17  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  *  Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  *  or visit www.oracle.com if you need additional information or have any
  21  *  questions.
  22  */
  23 
  24 package com.sun.tools.jextract;
  25 
  26 import java.nio.file.Files;
  27 import java.nio.file.Path;
  28 import java.util.ArrayList;
  29 import java.util.LinkedHashMap;
  30 import java.util.List;
  31 import java.util.Map;
  32 import java.util.logging.Level;
  33 
  34 public class HeaderResolver {
  35 
  36     // The folder path mapping to package name
  37     private final Map<Path, String> pkgMap = new LinkedHashMap<>();
  38     // The header file parsed
  39     private final Map<Path, HeaderFile> headerMap = new LinkedHashMap<>();
  40     private final Log log;
  41     private final Path builtinHeader;
  42 
  43     public HeaderResolver(Context ctx) {
  44         this.log = ctx.log;
  45         usePackageForFolder(Context.getBuiltinHeadersDir(), "clang_support");
  46         this.builtinHeader = Context.getBuiltinHeaderFile();
  47         ctx.sources.stream()
  48                 .map(Path::getParent)
  49                 .forEach(p -> usePackageForFolder(p, ctx.options.targetPackage));
  50         ctx.options.pkgMappings.forEach(this::usePackageForFolder);
  51     }
  52 
  53     public String headerInterfaceName(String filename) {
  54         return staticForwarderName(filename) + "_";


  55     }
  56 
  57     public String staticForwarderName(String filename) {
  58         int ext = filename.lastIndexOf('.');
  59         String name = ext != -1 ? filename.substring(0, ext) : filename;
  60         name = Utils.toClassName(name);
  61         return name + "_h";
  62     }
  63 
  64     private void usePackageForFolder(Path folder, String pkg) {
  65         folder = folder.normalize().toAbsolutePath();
  66         String existing = pkgMap.putIfAbsent(folder, pkg);
  67         final String finalFolder = (null == folder) ? "all folders not configured" : folder.toString();
  68         if (existing == null) {
  69             log.print(Level.CONFIG, () -> "Package " + pkg + " is selected for " + finalFolder);
  70         } else {
  71             String pkgName = pkg.isEmpty() ? "<default-package>" : pkg;
  72             log.print(Level.WARNING, () -> "Package " + existing + " had been selected for " + finalFolder + ", request to use " + pkgName + " is ignored.");
  73         }
  74     }
  75 
  76     // start of header file resolution logic
  77 
  78     static class HeaderPath {
  79         final String pkg;
  80         final String headerCls;
  81         final String forwarderCls;
  82 
  83         HeaderPath(String pkg, String headerCls, String forwarderCls) {
  84             this.pkg = pkg;
  85             this.headerCls = headerCls;
  86             this.forwarderCls = forwarderCls;
  87         }
  88     }
  89 
  90     /**
  91      * Determine package and interface name given a path. If the path is
  92      * a folder, then only package name is determined. The package name is
  93      * determined with the longest path matching the setup. If the path is not
  94      * setup for any package, the default package name is returned.
  95      *
  96      * @param origin The source path
  97      * @return The HeaderPath
  98      * @see Context::usePackageForFolder(Path, String)
  99      */
 100     private HeaderPath resolveHeaderPath(Path origin) {
 101         // normalize to absolute path
 102         origin = origin.normalize().toAbsolutePath();
 103         if (Files.isDirectory(origin)) {
 104             throw new IllegalStateException("Not an header file: " + origin);
 105         }
 106         String filename = origin.getFileName().toString();
 107         origin = origin.getParent();
 108         Path path = origin;
 109 
 110         // search the map for a hit with longest path
 111         while (path != null && !pkgMap.containsKey(path)) {
 112             path = path.getParent();
 113         }
 114 
 115         String pkg;
 116         if (path != null) {
 117             pkg = pkgMap.get(path);
 118             if (path.getNameCount() != origin.getNameCount()) {
 119                 String sep = pkg.isEmpty() ? "" : ".";
 120                 for (int i = path.getNameCount() ; i < origin.getNameCount() ; i++) {
 121                     pkg += sep + Utils.toJavaIdentifier(origin.getName(i).toString());
 122                     sep = ".";
 123                 }
 124                 usePackageForFolder(origin, pkg);
 125             }
 126         } else {
 127             //infer a package name from path
 128             List<String> parts = new ArrayList<>();
 129             for (Path p : origin) {
 130                 parts.add(Utils.toJavaIdentifier(p.toString()));
 131             }
 132             pkg = String.join(".", parts);
 133             usePackageForFolder(origin, pkg);
 134         }
 135 
 136         return new HeaderPath(pkg, headerInterfaceName(filename), staticForwarderName(filename));
 137     }
 138 
 139     private HeaderFile getHeaderFile(Path header) {
 140         if (!Files.isRegularFile(header)) {
 141             log.print(Level.WARNING, () -> "Not a regular file: " + header.toString());
 142             throw new IllegalArgumentException(header.toString());
 143         }
 144 
 145         final HeaderPath hp = resolveHeaderPath(header);
 146         return new HeaderFile(this, header, hp.pkg, hp.headerCls, hp.forwarderCls);
 147     }
 148 
 149     public HeaderFile headerFor(Path path) {
 150         if (path == null) {
 151             path = builtinHeader;
 152         }
 153 
 154         return headerMap.computeIfAbsent(path.normalize().toAbsolutePath(), this::getHeaderFile);
 155     }
 156 }
--- EOF ---