001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.launching.sourcelookup;
011:
012: import java.io.IOException;
013: import java.io.StringReader;
014: import java.util.Enumeration;
015: import java.util.HashMap;
016: import java.util.Iterator;
017: import java.util.zip.ZipEntry;
018: import java.util.zip.ZipFile;
019:
020: import javax.xml.parsers.DocumentBuilder;
021: import javax.xml.parsers.DocumentBuilderFactory;
022: import javax.xml.parsers.ParserConfigurationException;
023:
024: import org.eclipse.core.runtime.CoreException;
025: import org.eclipse.core.runtime.IPath;
026: import org.eclipse.core.runtime.IStatus;
027: import org.eclipse.core.runtime.Path;
028: import org.eclipse.core.runtime.PlatformObject;
029: import org.eclipse.core.runtime.Status;
030: import org.eclipse.debug.core.DebugPlugin;
031: import org.eclipse.jdt.internal.launching.LaunchingMessages;
032: import org.eclipse.jdt.internal.launching.LaunchingPlugin;
033: import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
034: import org.w3c.dom.Document;
035: import org.w3c.dom.Element;
036: import org.xml.sax.InputSource;
037: import org.xml.sax.SAXException;
038: import org.xml.sax.helpers.DefaultHandler;
039:
040: import com.ibm.icu.text.MessageFormat;
041:
042: /**
043: * Locates source elements in an archive (zip) in the local file system. Returns
044: * instances of <code>ZipEntryStorage</code>.
045: * <p>
046: * This class may be instantiated; it is not intended to be subclassed.
047: * </p>
048: * @see IJavaSourceLocation
049: * @since 2.0
050: * @deprecated In 3.0, the debug platform provides source lookup facilities that
051: * should be used in place of the Java source lookup support provided in 2.0.
052: * The new facilities provide a source lookup director that coordinates source
053: * lookup among a set of participants, searching a set of source containers.
054: * See the following packages: <code>org.eclipse.debug.core.sourcelookup</code>
055: * and <code>org.eclipse.debug.core.sourcelookup.containers</code>. This class
056: * has been replaced by the following classes:
057: * <code>org.eclipse.debug.core.sourcelookup.containers.ArchiveSourceContainer</code>
058: * and <code>org.eclipse.debug.core.sourcelookup.containers.ExternalArchiveSourceContainer</code>.
059: */
060: public class ArchiveSourceLocation extends PlatformObject implements
061: IJavaSourceLocation {
062:
063: /**
064: * Cache of shared zip files. Zip files are closed
065: * when the launching plug-in is shutdown.
066: */
067: private static HashMap fZipFileCache = new HashMap(5);
068:
069: /**
070: * Returns a zip file with the given name
071: *
072: * @param name zip file name
073: * @return The zip file with the given name
074: * @exception IOException if unable to create the specified zip
075: * file
076: */
077: private static ZipFile getZipFile(String name) throws IOException {
078: synchronized (fZipFileCache) {
079: ZipFile zip = (ZipFile) fZipFileCache.get(name);
080: if (zip == null) {
081: zip = new ZipFile(name);
082: fZipFileCache.put(name, zip);
083: }
084: return zip;
085: }
086: }
087:
088: /**
089: * Closes all zip files that have been opened,
090: * and removes them from the zip file cache.
091: * This method is only to be called by the launching
092: * plug-in.
093: */
094: public static void closeArchives() {
095: synchronized (fZipFileCache) {
096: Iterator iter = fZipFileCache.values().iterator();
097: while (iter.hasNext()) {
098: ZipFile file = (ZipFile) iter.next();
099: synchronized (file) {
100: try {
101: file.close();
102: } catch (IOException e) {
103: LaunchingPlugin.log(e);
104: }
105: }
106: }
107: fZipFileCache.clear();
108: }
109: }
110:
111: /**
112: * The root source folder in the archive
113: */
114: private IPath fRootPath;
115:
116: /**
117: * Whether the root path has been detected (or set)
118: */
119: private boolean fRootDetected = false;
120:
121: /**
122: * The name of the archive
123: */
124: private String fName;
125:
126: /**
127: * Constructs a new empty source location to be initialized with
128: * a memento.
129: */
130: public ArchiveSourceLocation() {
131: }
132:
133: /**
134: * Constructs a new source location that will retrieve source
135: * elements from the zip file with the given name.
136: *
137: * @param archiveName zip file
138: * @param sourceRoot a path to the root source folder in the
139: * specified archive, or <code>null</code> if the root source folder
140: * is the root of the archive
141: */
142: public ArchiveSourceLocation(String archiveName, String sourceRoot) {
143: super ();
144: setName(archiveName);
145: setRootPath(sourceRoot);
146: }
147:
148: /* (non-Javadoc)
149: * @see org.eclipse.jdt.launching.sourcelookup.IJavaSourceLocation#findSourceElement(java.lang.String)
150: */
151: public Object findSourceElement(String name) throws CoreException {
152: try {
153: if (getArchive() == null) {
154: return null;
155: }
156:
157: boolean possibleInnerType = false;
158: String pathStr = name.replace('.', '/');
159: int lastSlash = pathStr.lastIndexOf('/');
160: String typeName = pathStr;
161: do {
162: IPath entryPath = new Path(typeName + ".java"); //$NON-NLS-1$
163: autoDetectRoot(entryPath);
164: if (getRootPath() != null) {
165: entryPath = getRootPath().append(entryPath);
166: }
167: ZipEntry entry = getArchive().getEntry(
168: entryPath.toString());
169: if (entry != null) {
170: return new ZipEntryStorage(getArchive(), entry);
171: }
172: int index = typeName.lastIndexOf('$');
173: if (index > lastSlash) {
174: typeName = typeName.substring(0, index);
175: possibleInnerType = true;
176: } else {
177: possibleInnerType = false;
178: }
179: } while (possibleInnerType);
180: return null;
181: } catch (IOException e) {
182: throw new CoreException(
183: new Status(
184: IStatus.ERROR,
185: LaunchingPlugin.getUniqueIdentifier(),
186: IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR,
187: MessageFormat
188: .format(
189: LaunchingMessages.ArchiveSourceLocation_Unable_to_locate_source_element_in_archive__0__1,
190: new String[] { getName() }),
191: e));
192: }
193: }
194:
195: /**
196: * Automatically detect the root path, if required.
197: *
198: * @param path source file name, excluding root path
199: * @throws CoreException if unable to detect the root path for this source archive
200: */
201: private void autoDetectRoot(IPath path) throws CoreException {
202: if (!fRootDetected) {
203: ZipFile zip = null;
204: try {
205: zip = getArchive();
206: } catch (IOException e) {
207: throw new CoreException(
208: new Status(
209: IStatus.ERROR,
210: LaunchingPlugin.getUniqueIdentifier(),
211: IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR,
212: MessageFormat
213: .format(
214: LaunchingMessages.ArchiveSourceLocation_Exception_occurred_while_detecting_root_source_directory_in_archive__0__1,
215: new String[] { getName() }),
216: e));
217: }
218: synchronized (zip) {
219: Enumeration entries = zip.entries();
220: String fileName = path.toString();
221: try {
222: while (entries.hasMoreElements()) {
223: ZipEntry entry = (ZipEntry) entries
224: .nextElement();
225: String entryName = entry.getName();
226: if (entryName.endsWith(fileName)) {
227: int rootLength = entryName.length()
228: - fileName.length();
229: if (rootLength > 0) {
230: String root = entryName.substring(0,
231: rootLength);
232: setRootPath(root);
233: }
234: fRootDetected = true;
235: return;
236: }
237: }
238: } catch (IllegalStateException e) {
239: throw new CoreException(
240: new Status(
241: IStatus.ERROR,
242: LaunchingPlugin
243: .getUniqueIdentifier(),
244: IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR,
245: MessageFormat
246: .format(
247: LaunchingMessages.ArchiveSourceLocation_Exception_occurred_while_detecting_root_source_directory_in_archive__0__2,
248: new String[] { getName() }),
249: e));
250: }
251: }
252: }
253: }
254:
255: /**
256: * Returns the archive associated with this source
257: * location.
258: *
259: * @return zip file
260: * @throws IOException if unable to create the zip
261: * file associated with this location
262: */
263: protected ZipFile getArchive() throws IOException {
264: return getZipFile(getName());
265: }
266:
267: /**
268: * Sets the location of the root source folder within
269: * the archive, or <code>null</code> if the root source
270: * folder is the root of the archive
271: *
272: * @param path the location of the root source folder within
273: * the archive, or <code>null</code> if the root source
274: * folder is the root of the archive
275: */
276: private void setRootPath(String path) {
277: if (path == null || path.trim().length() == 0) {
278: fRootPath = null;
279: } else {
280: fRootPath = new Path(path);
281: fRootDetected = true;
282: }
283: }
284:
285: /**
286: * Returns the location of the root source folder within
287: * the archive, or <code>null</code> if the root source
288: * folder is the root of the archive
289: *
290: * @return the location of the root source folder within
291: * the archive, or <code>null</code> if the root source
292: * folder is the root of the archive
293: */
294: public IPath getRootPath() {
295: return fRootPath;
296: }
297:
298: /**
299: * Returns the name of the archive associated with this
300: * source location
301: *
302: * @return the name of the archive associated with this
303: * source location
304: */
305: public String getName() {
306: return fName;
307: }
308:
309: /**
310: * Sets the name of the archive associated with this
311: * source location
312: *
313: * @param name the name of the archive associated with this
314: * source location
315: */
316: private void setName(String name) {
317: fName = name;
318: }
319:
320: /* (non-Javadoc)
321: * @see java.lang.Object#equals(java.lang.Object)
322: */
323: public boolean equals(Object object) {
324: return object instanceof ArchiveSourceLocation
325: && getName().equals(
326: ((ArchiveSourceLocation) object).getName());
327: }
328:
329: /* (non-Javadoc)
330: * @see java.lang.Object#hashCode()
331: */
332: public int hashCode() {
333: return getName().hashCode();
334: }
335:
336: /* (non-Javadoc)
337: * @see org.eclipse.jdt.launching.sourcelookup.IJavaSourceLocation#getMemento()
338: */
339: public String getMemento() throws CoreException {
340: Document doc = DebugPlugin.newDocument();
341: Element node = doc.createElement("archiveSourceLocation"); //$NON-NLS-1$
342: doc.appendChild(node);
343: node.setAttribute("archivePath", getName()); //$NON-NLS-1$
344: if (getRootPath() != null) {
345: node.setAttribute("rootPath", getRootPath().toString()); //$NON-NLS-1$
346: }
347:
348: return DebugPlugin.serializeDocument(doc);
349: }
350:
351: /* (non-Javadoc)
352: * @see org.eclipse.jdt.launching.sourcelookup.IJavaSourceLocation#initializeFrom(java.lang.String)
353: */
354: public void initializeFrom(String memento) throws CoreException {
355: Exception ex = null;
356: try {
357: Element root = null;
358: DocumentBuilder parser = DocumentBuilderFactory
359: .newInstance().newDocumentBuilder();
360: parser.setErrorHandler(new DefaultHandler());
361: StringReader reader = new StringReader(memento);
362: InputSource source = new InputSource(reader);
363: root = parser.parse(source).getDocumentElement();
364:
365: String path = root.getAttribute("archivePath"); //$NON-NLS-1$
366: if (isEmpty(path)) {
367: abort(
368: LaunchingMessages.ArchiveSourceLocation_Unable_to_initialize_source_location___missing_archive_path__3,
369: null);
370: }
371: String rootPath = root.getAttribute("rootPath"); //$NON-NLS-1$
372:
373: setName(path);
374: setRootPath(rootPath);
375: return;
376: } catch (ParserConfigurationException e) {
377: ex = e;
378: } catch (SAXException e) {
379: ex = e;
380: } catch (IOException e) {
381: ex = e;
382: }
383: abort(
384: LaunchingMessages.ArchiveSourceLocation_Exception_occurred_initializing_source_location__5,
385: ex);
386: }
387:
388: private boolean isEmpty(String string) {
389: return string == null || string.length() == 0;
390: }
391:
392: /*
393: * Throws an internal error exception
394: */
395: private void abort(String message, Throwable e)
396: throws CoreException {
397: IStatus s = new Status(IStatus.ERROR, LaunchingPlugin
398: .getUniqueIdentifier(),
399: IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR,
400: message, e);
401: throw new CoreException(s);
402: }
403: }
|