1/*
2 * Copyright (c) 2012, 2014, 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
26package com.sun.tools.jdeps;
27
28import com.sun.tools.classfile.Dependency.Location;
29
30import java.io.Closeable;
31import java.io.IOException;
32import java.io.UncheckedIOException;
33import java.net.URI;
34import java.nio.file.Files;
35import java.nio.file.Path;
36import java.nio.file.Paths;
37import java.util.HashSet;
38import java.util.Map;
39import java.util.Objects;
40import java.util.Set;
41import java.util.concurrent.ConcurrentHashMap;
42import java.util.stream.Stream;
43
44/**
45 * Represents the source of the class files.
46 */
47public class Archive implements Closeable {
48    public static Archive getInstance(Path p, Runtime.Version version) {
49        try {
50            return new Archive(p, ClassFileReader.newInstance(p, version));
51        } catch (IOException e) {
52            throw new UncheckedIOException(e);
53        }
54    }
55
56    private final URI location;
57    private final Path path;
58    private final String filename;
59    private final ClassFileReader reader;
60
61    protected Map<Location, Set<Location>> deps = new ConcurrentHashMap<>();
62
63    protected Archive(String name) {
64        this(name, null, null);
65    }
66    protected Archive(String name, URI location, ClassFileReader reader) {
67        this.location = location;
68        this.path = location != null ? Paths.get(location) : null;
69        this.filename = name;
70        this.reader = reader;
71    }
72    protected Archive(Path p, ClassFileReader reader) {
73        this.location = null;
74        this.path = p;
75        this.filename = path.getFileName().toString();
76        this.reader = reader;
77    }
78
79    public ClassFileReader reader() {
80        return reader;
81    }
82
83    public String getName() {
84        return filename;
85    }
86
87    public Module getModule() {
88        return Module.UNNAMED_MODULE;
89    }
90
91    public boolean contains(String entry) {
92        return reader.entries().contains(entry);
93    }
94
95    public void addClass(Location origin) {
96        deps.computeIfAbsent(origin, _k -> new HashSet<>());
97    }
98
99    public void addClass(Location origin, Location target) {
100        deps.computeIfAbsent(origin, _k -> new HashSet<>()).add(target);
101    }
102
103    public Set<Location> getClasses() {
104        return deps.keySet();
105    }
106
107    public Stream<Location> getDependencies() {
108        return deps.values().stream()
109                   .flatMap(Set::stream);
110    }
111
112    public boolean hasDependences() {
113        return getDependencies().count() > 0;
114    }
115
116    public void visitDependences(Visitor v) {
117        for (Map.Entry<Location,Set<Location>> e: deps.entrySet()) {
118            for (Location target : e.getValue()) {
119                v.visit(e.getKey(), target);
120            }
121        }
122    }
123
124    /**
125     * Tests if any class has been parsed.
126     */
127    public boolean isEmpty() {
128        return getClasses().isEmpty();
129    }
130
131    public String getPathName() {
132        return path != null ? path.toString() : filename;
133    }
134
135    @Override
136    public int hashCode() {
137        return Objects.hash(this.filename, this.path);
138    }
139
140    @Override
141    public boolean equals(Object o) {
142        if (o instanceof Archive) {
143            Archive other = (Archive)o;
144            if (path == other.path || isSameLocation(this, other))
145                return true;
146        }
147        return false;
148    }
149
150    @Override
151    public String toString() {
152        return filename;
153    }
154
155    public Path path() {
156        return path;
157    }
158
159    public static boolean isSameLocation(Archive archive, Archive other) {
160        if (archive.path == null || other.path == null)
161            return false;
162
163        if (archive.location != null && other.location != null &&
164                archive.location.equals(other.location)) {
165            return true;
166        }
167
168        if (archive.isJrt() || other.isJrt()) {
169            return false;
170        }
171
172        try {
173            return Files.isSameFile(archive.path, other.path);
174        } catch (IOException e) {
175            throw new UncheckedIOException(e);
176        }
177    }
178
179    private boolean isJrt() {
180        return location != null && location.getScheme().equals("jrt");
181    }
182
183    @Override
184    public void close() throws IOException {
185        if (reader != null)
186            reader.close();
187    }
188
189    interface Visitor {
190        void visit(Location origin, Location target);
191    }
192}
193