Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Netbeans/nbbuild/antsrc/org/netbeans/nbbuild/   (Apache JAVA IDE Version 28©)  Datei vom 3.10.2025 mit Größe 25 kB image not shown  

Quelle  LayerIndex.java   Sprache: JAVA

 
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.netbeans.nbbuild;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import javax.xml.parsers.SAXParserFactory;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.resources.ZipResource;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Task to scan all XML layers in a NB installation
 * and report on which modules registers which files.
 * @author Jesse Glick
 */

public class LayerIndex extends Task {

    public LayerIndex() {}

    List<FileSet> filesets = new ArrayList<>();
    public void addConfiguredModules(FileSet fs) {
        filesets.add(fs);
    }

    private File output;
    public void setOutput(File f) {
        output = f;
    }

    private File serviceOutput;
    public void setServiceOutput(File f) {
        serviceOutput = f;
    }

    private String resourceId;
    private List<Resource> resources;
    /** If this parameter is provided, then this tasks creates a resource
     * composed from all the layerfiles and makes it accessible under this refId
     * @param id the refId to associate the collection with
     */

    public void setResourceId(String id) {
        resourceId = id;
        resources = new ZipArray();
    }

    @Override
    public void execute() throws BuildException {
        if (filesets.isEmpty()) {
            throw new BuildException();
        }
        SortedMap<String,String> files = new TreeMap<>(); // layer path -> cnb
        SortedMap<String,SortedMap<String,String>> labels = new TreeMap<>(); // layer path -> cnb -> label
        final Map<String,Integer> positions = new TreeMap<>(); // layer path -> position
        SortedMap<String,SortedMap<String,Set<String>>> serviceImpls = new TreeMap<>(); // path -> interface -> [impl]
        Map<String,Integer> servicePositions = new HashMap<>(); // impl -> position
        for (FileSet fs : filesets) {
            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
            File basedir = ds.getBasedir();
            for (String path : ds.getIncludedFiles()) {
                File jar = new File(basedir, path);
                try {
                    try (JarFile jf = new JarFile(jar)) {
                        Manifest mf = jf.getManifest();
                        if (mf == null) {
                            continue;
                        }
                        String modname = JarWithModuleAttributes.extractCodeName(mf.getMainAttributes());
                        if (modname == null) {
                            continue;
                        }
                        // XXX services.txt has e.g. "SERVICE org.openide.filesystems.MIMEResolver\n PROVIDER org.netbeans.modules.java.hints.test.Utilities$JavaMimeResolver"
                        // which is misleading since this pseudomodule is used only in unit tests
                        // maybe define Normally-Disabled: true in manifest.mf and skip from here (and disabledAutoloads)?
                        String cnb = modname.replaceFirst("/\\d+$""");
                        String layer = mf.getMainAttributes().getValue("OpenIDE-Module-Layer");
                        if (layer != null) {
                            if (resources != null) {
                                ZipResource res = new LayerResource(jar, layer, layer.replaceFirst("/[^/]+$""").replace('/''.') + ".xml");
                                resources.add(res);
                            } else {
                                parse(jf.getInputStream(jf.getEntry(layer)), files, labels, positions, cnb, jf);
                            }
                        }
                        ZipEntry generatedLayer = jf.getEntry("META-INF/generated-layer.xml");
                        if (generatedLayer != null) {
                            if (resources != null) {
                                ZipResource res = new LayerResource(jar, generatedLayer.getName(), cnb + "-generated.xml");
                                resources.add(res);
                            } else {
                                parse(jf.getInputStream(generatedLayer), files, labels, positions, cnb + "@", jf);
                            }
                        }
                        if (serviceOutput != null) {
                            // Could remember CNBs too.
                            parseServices(jf, serviceImpls, servicePositions);
                        }
                    }
                } catch (Exception x) {
                    throw new BuildException("Reading " + jar + ": " + x, x, getLocation());
                }
            }
        }

        if (resources != null) {
            assignReferences();
            return;
        }

        try {
            writeLayerIndex(files, positions, labels);
            if (serviceOutput != null) {
                writeServiceIndex(serviceImpls, servicePositions);
            }
        } catch (IOException x) {
            throw new BuildException(x, getLocation());
        }
    }

    @SuppressWarnings("unchecked")
    private void assignReferences() {
        getProject().getReferences().put(resourceId, resources);
    }

    static String shortenCNB(String cnb) {
        if (cnb != null) {
            return cnb.replaceFirst("^org\\.netbeans\\.""o.n.").replaceFirst("^org\\.openide\\.""o.o.").replaceFirst("\\.modules\\."".m.");
        } else {
            return "";
        }
    }

    private String shortenPath(String path) {
        return path.replaceAll("(^|/)org-netbeans-""$1o-n-").replaceAll("(^|/)org-openide-""$1o-o-").replaceAll("-modules-""-m-")
                .replaceAll("(^|/)org\\.netbeans\\.""$1o.n.").replaceAll("(^|/)org\\.openide\\.""$1o.o.").replaceAll("\\.modules\\."".m.");
    }

    private void parse(InputStream is, final Map<String,String> files, final SortedMap<String,SortedMap<String,String>> labels,
            final Map<String,Integer> positions, final String cnb, final JarFile jf) throws Exception {
        SAXParserFactory f = SAXParserFactory.newInstance();
        f.setValidating(false);
        f.setNamespaceAware(false);
        f.newSAXParser().parse(is, new DefaultHandler() {
            String prefix = "";
            void register(String path) {
                if (!files.containsKey(path)) {
                    files.put(path, cnb);
                } else if (!cnb.equals(files.get(path))) {
                    // Possibly >1 owner, but consider layer.xml vs. generated-layer.xml.
                    if (cnb.equals(files.get(path) + "@")) {
                        // leave alone
                    } else if ((cnb + "@").equals(files.get(path))) { // mark as defined in layer.xml
                        files.put(path, cnb);
                    } else { // different modules
                        files.put(path, null);
                    }
                }
            }
            @Override
            public void startElement(String uri, String localName, String qName,  Attributes attributes) throws SAXException {
                if (qName.equals("folder")) {
                    String n = attributes.getValue("name");
                    prefix += n + "/";
                    register(prefix);
                } else if (qName.equals("file")) {
                    String n = attributes.getValue("name");
                    prefix += n;
                    register(prefix);
                } else if (qName.equals("attr") && attributes.getValue("name").equals("SystemFileSystem.localizingBundle")) {
                    String bundle = attributes.getValue("stringvalue");
                    if (bundle != null) {
                        loadDisplayName(bundle,prefix.replaceAll("/$"""));
                    } else {
                        log("No stringvalue for SystemFileSystem.localizingBundle on " + prefix + " in " + cnb, Project.MSG_WARN);
                    }
                } else if (qName.equals("attr") && attributes.getValue("name").equals("displayName")) {
                    String bundleKey = attributes.getValue("bundlevalue");
                    if (bundleKey != null) {
                        String[] bundlevalue = bundleKey.split("#", 2);
                        loadDisplayName(bundlevalue[0], bundlevalue[1]);
                    } else {
                        String literal = attributes.getValue("stringvalue");
                        if (literal != null) {
                            loadDisplayName(literal);
                        }
                    }
                } else if (qName.equals("attr") && attributes.getValue("name").equals("position")) {
                    String intvalue = attributes.getValue("intvalue");
                    if (intvalue != null && /* #107550 */ !intvalue.equals("0")) {
                        try {
                            positions.put(prefix, Integer.parseInt(intvalue));
                        } catch (NumberFormatException x) {
                            throw new SAXException(x);
                        }
                    }
                }
            }
            private void loadDisplayName(String bundle, String key) throws SAXException {
                Properties props = new Properties();
                try {
                    ZipEntry entry = jf.getEntry(bundle.replace('.''/') + ".properties");
                    if (entry == null) {
                        /* Should be covered by ValidateLayerConsistencyTest.testLocalizingBundles:
                        log(bundle + " not found in reference from " + prefix + " in " + cnb, Project.MSG_WARN);
                         */

                        return;
                    }
                    props.load(jf.getInputStream(entry));
                } catch (IOException x) {
                    throw new SAXException(x);
                }
                String label = props.getProperty(key);
                if (label == null) {
                    /* Should be covered by ValidateLayerConsistencyTest.testLocalizingBundles:
                    log("Key " + key + " not found in " + bundle + " from " + cnb, Project.MSG_WARN);
                     */

                    return;
                }
                loadDisplayName(label);
            }
            private void loadDisplayName(String label) {
                SortedMap<String,String> cnb2label = labels.get(prefix);
                if (cnb2label == null) {
                    cnb2label = new TreeMap<>();
                    labels.put(prefix, cnb2label);
                }
                cnb2label.put(cnb, label);
            }
            @Override
            public void endElement(String uri, String localName, String qName) throws SAXException {
                if (qName.equals("folder")) {
                    prefix = prefix.replaceFirst("[^/]+/$""");
                } else if (qName.equals("file")) {
                    prefix = prefix.replaceFirst("[^/]+$""");
                }
            }
            @Override
            public InputSource resolveEntity(String pub, String sys) throws IOException, SAXException {
                return new InputSource(new StringReader(""));
            }
        });
    }

    private void parseServices(JarFile jf, SortedMap<String,SortedMap<String,Set<String>>> serviceImplsByPath,
            Map<String,Integer> servicePositions) throws IOException {
        Enumeration<JarEntry> entries = jf.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            if (entry.isDirectory()) {
                continue;
            }
            String name = entry.getName();
            String path, xface;
            if (name.startsWith("META-INF/services/")) {
                path = "";
                xface = name.substring("META-INF/services/".length());
            } else if (name.startsWith("META-INF/namedservices/")) {
                String rest = name.substring("META-INF/namedservices/".length());
                int x = rest.lastIndexOf('/');
                path = rest.substring(0, x);
                xface = rest.substring(x + 1);
            } else {
                continue;
            }
            try (InputStream is = jf.getInputStream(entry)) {
                BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                String lastImpl = null;
                String line;
                while ((line = r.readLine()) != null) {
                    if (line.startsWith("#position=") && lastImpl != null) {
                        servicePositions.put(lastImpl, Integer.parseInt(line.substring("#position=".length())));
                    } else if (line.startsWith("#-") || (line.length() > 0 && !line.startsWith("#"))) {
                        lastImpl = line;
                        SortedMap<String,Set<String>> serviceImpls = serviceImplsByPath.get(path);
                        if (serviceImpls == null) {
                            serviceImpls = new TreeMap<>();
                            serviceImplsByPath.put(path, serviceImpls);
                        }
                        Set<String> impls = serviceImpls.get(xface);
                        if (impls == null) {
                            impls = new HashSet<>();
                            serviceImpls.put(xface, impls);
                        }
                        impls.add(lastImpl);
                    }
                }
            }
        }
    }

    private static final class ZipArray extends ArrayList<Resource>
    implements ResourceCollection {
        public boolean isFilesystemOnly() {
            return false;
        }

        @Override
        public Stream<Resource> stream() {
            return super.stream();
        }
    }

    private static final class LayerResource extends ZipResource {
        private final String name;
        
        private LayerResource(
            File jar, String path, String name) throws ZipException {
            super(jar, "UTF-8"new org.apache.tools.zip.ZipEntry(path));
            this.name = name;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public InputStream getInputStream() throws IOException {
            final ZipFile z = new ZipFile(getZipfile(), ZipFile.OPEN_READ);
            ZipEntry ze = z.getEntry(super.getName());
            if (ze == null) {
                z.close();
                throw new BuildException("no entry " + getName() + " in "
                                         + getArchive());
            }
            return z.getInputStream(ze);
        }

    }

    private void writeLayerIndex(SortedMap<String,String> files, final Map<String,Integer> positions,
            SortedMap<String,SortedMap<String,String>> labels) throws IOException {
        int maxlength = 0;
        for (String cnb : files.values()) {
            maxlength = Math.max(maxlength, shortenCNB(cnb).length());
        }
        PrintWriter pw = output != null ? new PrintWriter(output, "UTF-8") : null;
        Map<String,String> virtualEntries = computeMIMELookupEntries(files.keySet());
        updateMap(files, virtualEntries);
        updateMap(positions, virtualEntries);
        updateMap(labels, virtualEntries);
        SortedSet<String> layerPaths = new TreeSet<>(new LayerPathComparator(positions));
        layerPaths.addAll(files.keySet());
        SortedSet<String> remaining = new TreeSet<>(files.keySet());
        remaining.removeAll(layerPaths);
        assert remaining.isEmpty() : remaining;
        for (String path : layerPaths) {
            String cnb = files.get(path);
            String line = String.format("%-" + maxlength + "s %s", shortenCNB(cnb), shortenPath(path));
            if (virtualEntries.containsKey(path)) {
                line += " (merged)";
            }
            Integer pos = positions.get(path);
            if (pos != null) {
                line += String.format(" @%d", pos);
            }
            SortedMap<String,String> cnb2Label = labels.get(path);
            if (cnb2Label != null) {
                if (cnb2Label.size() == 1 && cnb2Label.keySet().iterator().next().equals(cnb)) {
                    line += String.format(" (\"%s\")", cnb2Label.values().iterator().next());
                } else {
                    for (Map.Entry<String,String> labelEntry : cnb2Label.entrySet()) {
                        line += String.format(" (%s: \"%s\")", shortenCNB(labelEntry.getKey()), labelEntry.getValue());
                    }
                }
            }
            if (pw != null) {
                pw.println(line);
            } else {
                log(line);
            }
        }
        if (pw != null) {
            pw.close();
        }
        if (output != null) {
            log(output + ": layer index written");
        }
    }

    /**
     * Map from virtual file paths to original literal file path.
     * E.g. Editors/text/html/Popup/foo.instance -> Editors/Popup/foo.instance
     * See ValidateLayerConsistencyTest.testFolderOrdering for comparison.
     */

    private Map<String,String> computeMIMELookupEntries(Set<String> files) {
        Pattern editorFolderPattern = Pattern.compile("Editors/(application|text)/([^/]+)(.*/)");
        Map<String,String> result = new HashMap<>();
        for (String editorFolder : files) {
            Matcher m = editorFolderPattern.matcher(editorFolder);
            if (!m.matches()) {
                continue;
            }
            // $0="Editors/text/html/Popup/" $1="text" $2="html" $3="/Popup/"
            List<String> prefixen = new ArrayList<>(2);
            prefixen.add("Editors" + m.group(3)); // "Editors/Popup/"
            if (m.group(2).endsWith("+xml")) { // Editors/text/x-ant+xml/Popup/
                prefixen.add("Editors/" + m.group(1) + "/xml" + m.group(3)); // Editors/text/xml/Popup/
            }
            for (String prefix : prefixen) {
                for (String file : files) {
                    if (file.startsWith(prefix)) { // "Editors/Popup/foo.instance"
                        String basename = file.substring(prefix.length());
                        if (basename.contains("/")) {
                            // Would technically be correct to show, but usually irrelevant.
                            continue;
                        }
                        String virtual = editorFolder + basename; // Editors/text/html/Popup/foo.instance
                        if (!files.contains(virtual)) {
                            result.put(virtual, file);
                        }
                    }
                }
            }
        }
        return result;
    }

    private <T> void updateMap(Map<String,T> map, Map<String,String> virtualEntries) {
        for (Map.Entry<String,String> entry : virtualEntries.entrySet()) {
            String orig = entry.getValue();
            if (map.containsKey(orig)) {
                map.put(entry.getKey(), map.get(orig));
            }
        }
    }

    private static class LayerPathComparator implements Comparator<String> {
        private final Map<String,Integer> positions;
        public LayerPathComparator(Map<String,Integer> positions) {
            this.positions = positions;
        }
        public int compare(String p1, String p2) {
            StringTokenizer tok1 = new StringTokenizer(p1, "/");
            StringTokenizer tok2 = new StringTokenizer(p2, "/");
            String prefix = "";
            while (tok1.hasMoreTokens()) {
                String piece1 = tok1.nextToken();
                if (tok2.hasMoreTokens()) {
                    String piece2 = tok2.nextToken();
                    if (piece1.equals(piece2)) {
                        prefix += piece1 + "/";
                    } else {
                        Integer pos1 = pos(prefix + piece1);
                        Integer pos2 = pos(prefix + piece2);
                        if (pos1 == null) {
                            if (pos2 == null) {
                                return piece1.compareTo(piece2);
                            } else {
                                return 1;
                            }
                        } else {
                            if (pos2 == null) {
                                return -1;
                            } else {
                                int diff = pos1 - pos2;
                                if (diff != 0) {
                                    return diff;
                                } else {
                                    return piece1.compareTo(piece2);
                                }
                            }
                        }
                    }
                } else {
                    return 1;
                }
            }
            if (tok2.hasMoreTokens()) {
                return -1;
            }
            assert p1.equals(p2) : p1 + " vs. " + p2;
            return 0;
        }
        Integer pos(String path) {
            return positions.containsKey(path) ? positions.get(path) : positions.get(path + "/");
        }
    }

    private void writeServiceIndex(SortedMap<String,SortedMap<String,Set<String>>> serviceImpls,
            final Map<String,Integer> servicePositions) throws IOException {
        try (PrintWriter pw = new PrintWriter(serviceOutput, "UTF-8")) {
            for (Map.Entry<String,SortedMap<String,Set<String>>> mainEntry : serviceImpls.entrySet()) {
                String path = mainEntry.getKey();
                for (Map.Entry<String,Set<String>> entry : mainEntry.getValue().entrySet()) {
                    pw.print("SERVICE " + entry.getKey());
                    if (path.length() > 0) {
                        pw.println(" under " + path);
                    } else {
                        pw.println();
                    }
                    SortedSet<String> impls = new TreeSet<>(new ServiceComparator(servicePositions));
                    impls.addAll(entry.getValue());
                    Set<String> masked = new HashSet<>();
                    for (String impl : impls) {
                        if (impl.startsWith("#-")) {
                            masked.add(impl);
                            masked.add(impl.substring(2));
                        }
                    }
                    impls.removeAll(masked);
                    for (String impl : impls) {
                        if (servicePositions.containsKey(impl)) {
                            impl += " @" + servicePositions.get(impl);
                        }
                        pw.println(" PROVIDER " + impl);
                    }
                }
            }
        }
        log(serviceOutput + ": service index written");
    }

    private static class ServiceComparator implements Comparator<String> {
        private final Map<String,Integer> servicePositions;
        public ServiceComparator(Map<String,Integer> servicePositions) {
            this.servicePositions = servicePositions;
        }
        public int compare(String i1, String i2) {
            Integer pos1 = servicePositions.get(i1);
            Integer pos2 = servicePositions.get(i2);
            if (pos1 == null) {
                if (pos2 == null) {
                    return i1.compareTo(i2);
                } else {
                    return 1;
                }
            } else {
                if (pos2 == null) {
                    return -1;
                } else {
                    int diff = pos1 - pos2;
                    if (diff != 0) {
                        return diff;
                    } else {
                        return i1.compareTo(i2);
                    }
                }
            }
        }
    }

}

Messung V0.5
C=96 H=99 G=97

¤ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.