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 traverse(Visitor visitor, Repository repo,
64 String name,
65 VersionConstraint constraint)
66 throws ModuleInitializationException{
67
68 traverse(visitor, 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 traverse(Visitor, visitor, 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(visitor, 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 traverse(Visitor visitor, 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 visitor.init(m);
141 traverse(visitor, m);
142 }
143 }
144
145 /**
146 * Invokes a visitor on each of the imports of the {@code module} The
147 * imports are visited in depth-first order with respect to the graph of
148 * interconnected modules. Note that the given module is the first one
149 * visited.
150 */
151 public void traverse(Visitor visitor, Module module) {
152 if (visited.contains(module)) {
153 return;
154 }
155 visited.add(module);
156 if (visitor.preVisit(module)) {
157 List<Module> imported = module.getImportedModules();
158 for (Module m : imported) {
159 visitor.visit(m);
160 traverse(visitor, m);
161 }
162 }
163 visitor.postVisit(module);
164 }
165
166 /**
167 * @return true if any modules were traversed, false otherwise
168 */
169 public boolean traversedAny() {
170 return visited.size() > 0;
171 }
172
173 /**
174 * @return an {@code Iterator} over the {@code Module} instances which have
175 * been visited; note that the list is ordered by the depth-first
176 * traversal of module imports.
177 */
178 public Iterator<Module> iterator() {
179 return visited.iterator();
180 }
181
182 abstract static class Visitor {
183 private final ImportTraverser traverser;
184
185 protected Visitor(ImportTraverser traverser) {
186 this.traverser = traverser;
187 }
188
189 /**
190 * @param m {@code Module} possibly visited by the traverser
191 * @return true if the given {@code Module} has been visited, false
192 * otherwise
193 */
194 protected boolean visited(Module m) {
195 return traverser.visited.contains(m);
196 }
197
198 /**
199 * Invoked with the {@code Module} identified by the client-called {@code
200 * visit} method before any other {@code Module} instances are visited.
201 * @param m First {@code Module} visited by the traverser
202 */
203 protected void init(Module m) { }
204
205 /**
206 * Invoked on a {@code Module} instance before its imports are visited.
207 * @param m {@code Module} which imports the module about to be visited by
208 * {@link #visit(Module)}
209 * @return true if the {@code Module} should be visited, false otherwise.
210 */
211 protected boolean preVisit(Module m) {
212 return true;
213 }
214
215 /**
216 * Invoked on a {@code Module} instance as it is visited
217 * @param m {@code Module} being visited
218 */
219 protected void visit(Module m) { }
220
221 /**
222 * Invoked on a {@code Module} instance after its imports are visited.
223 * @param m {@code Module} which imports the module that was just visited by
224 * {@link #visit(Module)}
225 */
226 protected void postVisit(Module m) { }
227 }
228 }