001: package org.andromda.repositories.mdr;
002:
003: import java.io.File;
004: import java.io.InputStream;
005:
006: import java.net.MalformedURLException;
007: import java.net.URL;
008:
009: import java.util.ArrayList;
010: import java.util.Collection;
011: import java.util.HashMap;
012:
013: import javax.jmi.reflect.RefPackage;
014:
015: import org.andromda.core.common.AndroMDALogger;
016: import org.andromda.core.common.ResourceUtils;
017: import org.apache.commons.lang.StringUtils;
018: import org.apache.log4j.Logger;
019: import org.netbeans.api.xmi.XMIInputConfig;
020: import org.netbeans.lib.jmi.xmi.XmiContext;
021:
022: /**
023: * This class supports the expansion of XML HREF references to other modules within a model. The result of the resolver
024: * should be a valid URL. This is necessary for Magic Draw as it doesn't have the entire model referenced but just the
025: * archived model.
026: *
027: * @author Matthias Bohlen
028: * @author Chad Brandon
029: */
030: public class MDRXmiReferenceResolverContext extends XmiContext {
031: private String[] moduleSearchPaths;
032: private static Logger logger = Logger
033: .getLogger(MDRXmiReferenceResolverContext.class);
034: private static final HashMap urlMap = new HashMap();
035:
036: /**
037: * Constructs an instance of this class.
038: *
039: * @param extents the extents.
040: * @param config the xml input config.
041: * @param moduleSearchPaths the paths to search for modules
042: */
043: public MDRXmiReferenceResolverContext(RefPackage[] extents,
044: XMIInputConfig config, String[] moduleSearchPaths) {
045: super (extents, config);
046: this .moduleSearchPaths = moduleSearchPaths;
047: }
048:
049: /**
050: * @see org.netbeans.lib.jmi.xmi.XmiContext#toURL(java.lang.String)
051: */
052: public URL toURL(final String systemId) {
053: if (logger.isDebugEnabled()) {
054: logger.debug("attempting to resolve Xmi Href --> '"
055: + systemId + "'");
056: }
057:
058: final String suffix = this .getSuffix(systemId);
059:
060: // if the model URL has a suffix of '.zip' or '.jar', get
061: // the suffix without it and store it in the urlMap
062: String exts = "\\.jar|\\.zip";
063: String suffixWithExt = suffix.replaceAll(exts, "");
064: URL modelUrl = (URL) urlMap.get(suffixWithExt);
065:
066: // Several tries to construct a URL that really exists.
067: if (modelUrl == null) {
068: // If systemId is a valid URL, simply use it
069: modelUrl = this .getValidURL(systemId);
070: if (modelUrl == null) {
071: // Try to find suffix in module list.
072: final String modelUrlAsString = this
073: .findModuleUrl(suffix);
074: if (StringUtils.isNotBlank(modelUrlAsString)) {
075: modelUrl = this .getValidURL(modelUrlAsString);
076: }
077: if (modelUrl == null) {
078: // search the classpath
079: modelUrl = this .findModelUrlOnClasspath(systemId);
080: }
081: if (modelUrl == null) {
082: // Give up and let superclass deal with it.
083: modelUrl = super .toURL(systemId);
084: }
085: }
086:
087: // if we've found the module model, log it
088: // and place it in the map so we don't have to
089: // find it if we need it again.
090: if (modelUrl != null) {
091: urlMap.put(suffixWithExt, modelUrl);
092: }
093: }
094: if (modelUrl != null
095: && !this .loggedReferencedModels.contains(modelUrl)) {
096: AndroMDALogger.info("referenced model --> '" + modelUrl
097: + "'");
098: this .loggedReferencedModels.add(modelUrl);
099: }
100: return modelUrl;
101: }
102:
103: /**
104: * Keeps track of the referenced models that have been logged.
105: */
106: private final Collection loggedReferencedModels = new ArrayList();
107:
108: /**
109: * Finds a module in the module search path.
110: *
111: * @param moduleName the name of the module without any path
112: * @return the complete URL string of the module if found (null if not found)
113: */
114: private final String findModuleUrl(final String moduleName) {
115: String moduleUrl = null;
116: if (this .moduleSearchPaths != null) {
117: if (logger.isDebugEnabled()) {
118: logger.debug("findModuleURL: moduleSearchPath.length="
119: + moduleSearchPaths.length);
120: }
121: for (int ctr = 0; ctr < moduleSearchPaths.length; ctr++) {
122: final String moduleSearchPath = moduleSearchPaths[ctr];
123: if (moduleSearchPath != null
124: && moduleSearchPath.length() > 0) {
125: if (moduleSearchPath.endsWith(moduleName)) {
126: moduleUrl = moduleSearchPath;
127: } else {
128: final File candidate = new File(
129: moduleSearchPath, moduleName);
130: if (logger.isDebugEnabled()) {
131: logger.debug("candidate '"
132: + candidate.toString()
133: + "' exists=" + candidate.exists());
134: }
135: if (candidate.exists()) {
136: try {
137: moduleUrl = candidate.toURL()
138: .toExternalForm();
139: } catch (final MalformedURLException exception) {
140: // ignore
141: }
142: }
143: }
144: if (moduleUrl != null
145: && moduleName.endsWith(".zip")
146: || moduleName.endsWith(".jar")) {
147: // - typical case for MagicDraw
148: moduleUrl = "jar:"
149: + moduleUrl
150: + "!/"
151: + moduleName.substring(0, moduleName
152: .length() - 4);
153: }
154: // - we've found a module
155: if (moduleUrl != null && moduleUrl.length() > 0) {
156: break;
157: }
158: }
159: }
160: }
161: return moduleUrl;
162: }
163:
164: /**
165: * Gets the suffix of the <code>systemId</code>
166: *
167: * @param systemId the system identifier.
168: * @return the suffix as a String.
169: */
170: private final String getSuffix(String systemId) {
171: int lastSlash = systemId.lastIndexOf("/");
172: if (lastSlash > 0) {
173: String suffix = systemId.substring(lastSlash + 1);
174: return suffix;
175: }
176: return systemId;
177: }
178:
179: /**
180: * The suffixes to use when searching for referenced models on the classpath.
181: */
182: protected final static String[] CLASSPATH_MODEL_SUFFIXES = new String[] {
183: "xml", "xmi" };
184:
185: /**
186: * Searches for the model URL on the classpath.
187: *
188: * @param systemId the system identifier.
189: * @return the suffix as a String.
190: */
191: private final URL findModelUrlOnClasspath(final String systemId) {
192: String modelName = StringUtils
193: .substringAfterLast(systemId, "/");
194: String dot = ".";
195:
196: // remove the first prefix because it may be an archive
197: // (like magicdraw)
198: modelName = StringUtils.substringBeforeLast(modelName, dot);
199:
200: URL modelUrl = null;
201: if (StringUtils.isNotBlank(modelName)) {
202: modelUrl = ResourceUtils.getResource(modelName);
203: if (modelUrl == null) {
204: if (CLASSPATH_MODEL_SUFFIXES != null
205: && CLASSPATH_MODEL_SUFFIXES.length > 0) {
206: int suffixNum = CLASSPATH_MODEL_SUFFIXES.length;
207: for (int ctr = 0; ctr < suffixNum; ctr++) {
208: if (logger.isDebugEnabled()) {
209: logger
210: .debug("searching for model reference --> '"
211: + modelUrl + "'");
212: }
213: String suffix = CLASSPATH_MODEL_SUFFIXES[ctr];
214: modelUrl = ResourceUtils.getResource(modelName
215: + dot + suffix);
216: if (modelUrl != null) {
217: break;
218: }
219: }
220: }
221: }
222: }
223: return modelUrl;
224: }
225:
226: /**
227: * Returns a URL if the systemId is valid. Returns null otherwise. Catches exceptions as necessary.
228: *
229: * @param systemId the system id
230: * @return the URL (if valid)
231: */
232: private final URL getValidURL(final String systemId) {
233: InputStream stream = null;
234: URL url = null;
235: try {
236: url = new URL(systemId);
237: stream = url.openStream();
238: stream.close();
239: } catch (final Exception exception) {
240: url = null;
241: } finally {
242: stream = null;
243: }
244: return url;
245: }
246: }
|