1 /*
2 * Copyright 2008 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package sun.module.tools;
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.module.Module;
31 import java.module.ModuleArchiveInfo;
32 import java.module.ModuleDefinition;
33 import java.module.ModuleInitializationException;
34 import java.module.Modules;
35 import java.module.Query;
36 import java.module.Repository;
37 import java.module.VersionConstraint;
38 import java.util.LinkedHashSet;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Set;
42 import sun.module.JamUtils;
43
44 /**
45 * Provides a means to visit each of a module's imports.
46 * @since 1.7
47 */
48 public class ImportTraverser implements Iterable<Module> {
49 private Set<Module> visited = new LinkedHashSet<Module>();
50
51 /**
52 * Invokes a visitor on each of the imports of the module identified by
53 * the parameters. The imports are visited in depth-first order with respect to
54 * the graph of interconnected modules. Note that the module identified
55 * by parameters is the first one visited.
56 *
57 * @param repo repository in which to find a named module
58 * @param name name of the module to find
59 * @param constraint version constraint on the module; may be null
60 * @throws ModuleInitializationException if the identified module cannot be
61 * instantiated
62 */
63 public void visit(Repository repo,
64 String name,
65 VersionConstraint constraint)
66 throws ModuleInitializationException{
67
68 visit(repo, name, constraint, null, null);
69 }
70
71 /**
72 * Invokes a visitor on each of the imports of the module identified by
73 * the parameters. The imports are visited in depth-first order with respect to
74 * the graph of interconnected modules. Note that the module identified
75 * by parameters is the first one visited.
76 *
77 * @param jamName name of a JAM file for which dependencies are returned
78 * @throws java.io.IOException if a Repository for the named JAM file cannot
79 * be created
80 * @throws ModuleInitializationException if the identified module cannot be
81 * instantiated
82 */
83 public void visit(String jamName)
84 throws IOException, ModuleInitializationException {
85
86 File repoDir = JamUtils.createTempDir();
87 Repository repo = Modules.newLocalRepository("importvisitor", repoDir);
88 ModuleArchiveInfo mai = repo.install(new File(jamName).toURI().toURL());
89
90 visit(repo, mai.getName(), mai.getVersion().toVersionConstraint(),
91 mai.getPlatform(), mai.getArch());
92 }
93
94 /**
95 * Invokes a visitor on each of the imports of the module identified by
96 * the parameters. The imports are visited in depth-first order with respect to
97 * the graph of interconnected modules. Note that the module identified
98 * by parameters is the first one visited.
99 *
100 * @param repo repository in which to find a named module
101 * @param name name of the module to find
102 * @param constraint version constraint on the module; may be null
103 * @param platform platform of the module to find; may be null
104 * @param arch architecture of the module to find; may be null
105 * @throws ModuleInitializationException if the identified module cannot be
106 * instantiated
107 * @throws IllegalArgumentException if one of {@code platform, arch} is
108 * null and the other is not, or if the {@code repository} finds more than
109 * one module matching the given parameters.
110 */
111 public void visit(Repository repo,
112 String name, VersionConstraint constraint,
113 String platform, String arch)
114 throws ModuleInitializationException {
115
116 if (repo == null || name == null) {
117 throw new NullPointerException();
118 }
119
120 if (platform == null ^ arch == null) {
121 throw new IllegalArgumentException(
122 "module platform and arch must be either both null or both non-null");
123 }
124
125 Query q = Query.module(
126 name, constraint == null ? VersionConstraint.DEFAULT : constraint);
127 if (platform != null) {
128 q = Query.and(
129 q, Query.annotation(java.module.annotation.PlatformBinding.class));
130 }
131
132 List<ModuleDefinition> mdList = repo.find(q);
133 if (mdList.size() > 1) {
134 throw new IllegalArgumentException(
135 "more than one matching module found for name=" + name
136 + " version=" + constraint
137 + " platform=" + platform + " arch=" + arch);
138 } else if (mdList.size() == 1) {
139 Module m = mdList.get(0).getModuleInstance();
140 init(m);
141 visit(repo, m);
142 }
143 }
144
145 public void visit(Repository repo,
146 Module module) {
147
148 if (visited.contains(module)) {
149 return;
150 }
151 visited.add(module);
152 if (preVisit(module)) {
153 List<Module> imported = module.getImportedModules();
154 for (Module m : imported) {
155 visit(m);
156 visit(repo, m);
157 }
158 }
159 postVisit(module);
160 }
161
162 /**
163 * @return an unmodifiable list of the {@code Module} instances which have
164 * been visited; note that the list is ordered by the depth-first
165 * traversal of module imports.
166 */
167 public Iterator<Module> iterator() {
168 return visited.iterator();
169 }
170
171 /**
172 * @param m {@code Module} possibly visited by the traverser
173 * @return true if the given {@code Module} has been visited, false
174 * otherwise
175 */
176 protected boolean visited(Module m) {
177 return visited.contains(m);
178 }
179
180 /**
181 * Invoked with the {@code Module} identified by the client-called {@code
182 * visit} method before any other {@code Module} instances are visited.
183 * @param m First {@code Module} visited by the traverser
184 */
185 protected void init(Module m) { }
186
187 /**
188 * Invoked on a {@code Module} instance before its imports are visited.
189 * @param m {@code Module} which imports the module about to be visited by
190 * {@link #visit(Module)}
191 * @return true if the {@code Module} should be visited, false otherwise.
192 */
193 protected boolean preVisit(Module m) {
194 return true;
195 }
196
197 /**
198 * Invoked on a {@code Module} instance as it is visited
199 * @param m {@code Module} being visited
200 */
201 protected void visit(Module m) { }
202
203 /**
204 * Invoked on a {@code Module} instance after its imports are visited.
205 * @param m {@code Module} which imports the module that was just visited by
206 * {@link #visit(Module)}
207 */
208 protected void postVisit(Module m) { }
209 }