001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 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.internal.ui.jarpackager;
011:
012: import java.io.BufferedInputStream;
013: import java.io.ByteArrayInputStream;
014: import java.io.IOException;
015: import java.io.InputStream;
016: import java.util.ArrayList;
017: import java.util.HashSet;
018: import java.util.List;
019: import java.util.Set;
020:
021: import javax.xml.parsers.DocumentBuilder;
022: import javax.xml.parsers.DocumentBuilderFactory;
023: import javax.xml.parsers.ParserConfigurationException;
024:
025: import org.eclipse.core.resources.IFile;
026: import org.eclipse.core.resources.IFolder;
027: import org.eclipse.core.resources.IProject;
028: import org.eclipse.core.resources.ResourcesPlugin;
029:
030: import org.eclipse.core.runtime.Assert;
031: import org.eclipse.core.runtime.CoreException;
032: import org.eclipse.core.runtime.IPath;
033: import org.eclipse.core.runtime.IStatus;
034: import org.eclipse.core.runtime.MultiStatus;
035: import org.eclipse.core.runtime.Path;
036: import org.eclipse.core.runtime.Status;
037:
038: import org.eclipse.ltk.core.refactoring.RefactoringCore;
039: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
040: import org.eclipse.ltk.core.refactoring.RefactoringDescriptorProxy;
041: import org.eclipse.ltk.core.refactoring.history.IRefactoringHistoryService;
042: import org.eclipse.ltk.core.refactoring.history.RefactoringHistory;
043:
044: import org.w3c.dom.Element;
045: import org.w3c.dom.Node;
046: import org.w3c.dom.NodeList;
047: import org.xml.sax.InputSource;
048: import org.xml.sax.SAXException;
049:
050: import org.eclipse.jdt.core.IJavaElement;
051: import org.eclipse.jdt.core.IPackageFragment;
052: import org.eclipse.jdt.core.IType;
053: import org.eclipse.jdt.core.JavaCore;
054:
055: import org.eclipse.jdt.internal.corext.util.Messages;
056:
057: import org.eclipse.jdt.ui.jarpackager.IJarDescriptionReader;
058: import org.eclipse.jdt.ui.jarpackager.JarPackageData;
059:
060: import org.eclipse.jdt.internal.ui.JavaPlugin;
061: import org.eclipse.jdt.internal.ui.IJavaStatusConstants;
062:
063: /**
064: * Reads data from an InputStream and returns a JarPackage
065: */
066: public class JarPackageReader extends Object implements
067: IJarDescriptionReader {
068:
069: protected InputStream fInputStream;
070:
071: private MultiStatus fWarnings;
072:
073: /**
074: * Reads a Jar Package from the underlying stream.
075: * It is the client's responsibility to close the stream.
076: */
077: public JarPackageReader(InputStream inputStream) {
078: Assert.isNotNull(inputStream);
079: fInputStream = new BufferedInputStream(inputStream);
080: fWarnings = new MultiStatus(
081: JavaPlugin.getPluginId(),
082: 0,
083: JarPackagerMessages.JarPackageReader_jarPackageReaderWarnings,
084: null);
085: }
086:
087: public void read(JarPackageData jarPackage) throws CoreException {
088: try {
089: readXML(jarPackage);
090: } catch (IOException ex) {
091: String message = (ex.getLocalizedMessage() != null ? ex
092: .getLocalizedMessage() : ""); //$NON-NLS-1$
093: throw new CoreException(new Status(IStatus.ERROR,
094: JavaPlugin.getPluginId(),
095: IJavaStatusConstants.INTERNAL_ERROR, message, ex));
096: } catch (SAXException ex) {
097: String message = (ex.getLocalizedMessage() != null ? ex
098: .getLocalizedMessage() : ""); //$NON-NLS-1$
099: throw new CoreException(new Status(IStatus.ERROR,
100: JavaPlugin.getPluginId(),
101: IJavaStatusConstants.INTERNAL_ERROR, message, ex));
102: }
103: }
104:
105: /**
106: * Closes this stream.
107: * It is the clients responsibility to close the stream.
108: *
109: * @exception CoreException
110: */
111: public void close() throws CoreException {
112: if (fInputStream != null)
113: try {
114: fInputStream.close();
115: } catch (IOException ex) {
116: String message = (ex.getLocalizedMessage() != null ? ex
117: .getLocalizedMessage() : ""); //$NON-NLS-1$
118: throw new CoreException(new Status(IStatus.ERROR,
119: JavaPlugin.getPluginId(),
120: IJavaStatusConstants.INTERNAL_ERROR, message,
121: ex));
122: }
123: }
124:
125: public JarPackageData readXML(JarPackageData jarPackage)
126: throws IOException, SAXException {
127: DocumentBuilderFactory factory = DocumentBuilderFactory
128: .newInstance();
129: factory.setValidating(false);
130: DocumentBuilder parser = null;
131:
132: try {
133: parser = factory.newDocumentBuilder();
134: } catch (ParserConfigurationException ex) {
135: throw new IOException(ex.getLocalizedMessage());
136: } finally {
137: // Note: Above code is OK since clients are responsible to close the stream
138: }
139: Element xmlJarDesc = parser
140: .parse(new InputSource(fInputStream))
141: .getDocumentElement();
142: if (!xmlJarDesc.getNodeName().equals(
143: JarPackagerUtil.DESCRIPTION_EXTENSION)) {
144: throw new IOException(
145: JarPackagerMessages.JarPackageReader_error_badFormat);
146: }
147: NodeList topLevelElements = xmlJarDesc.getChildNodes();
148: for (int i = 0; i < topLevelElements.getLength(); i++) {
149: Node node = topLevelElements.item(i);
150: if (node.getNodeType() != Node.ELEMENT_NODE)
151: continue;
152: Element element = (Element) node;
153: xmlReadJarLocation(jarPackage, element);
154: xmlReadOptions(jarPackage, element);
155: xmlReadRefactoring(jarPackage, element);
156: xmlReadSelectedProjects(jarPackage, element);
157: if (jarPackage.areGeneratedFilesExported())
158: xmlReadManifest(jarPackage, element);
159: xmlReadSelectedElements(jarPackage, element);
160: }
161: return jarPackage;
162: }
163:
164: private void xmlReadJarLocation(JarPackageData jarPackage,
165: Element element) {
166: if (element.getNodeName().equals(JarPackagerUtil.JAR_EXTENSION))
167: jarPackage.setJarLocation(Path.fromPortableString(element
168: .getAttribute("path"))); //$NON-NLS-1$
169: }
170:
171: private void xmlReadOptions(JarPackageData jarPackage,
172: Element element) throws java.io.IOException {
173: if (element.getNodeName().equals("options")) { //$NON-NLS-1$
174: jarPackage.setOverwrite(getBooleanAttribute(element,
175: "overwrite")); //$NON-NLS-1$
176: jarPackage.setCompress(getBooleanAttribute(element,
177: "compress")); //$NON-NLS-1$
178: jarPackage.setExportErrors(getBooleanAttribute(element,
179: "exportErrors")); //$NON-NLS-1$
180: jarPackage.setExportWarnings(getBooleanAttribute(element,
181: "exportWarnings")); //$NON-NLS-1$
182: jarPackage.setSaveDescription(getBooleanAttribute(element,
183: "saveDescription")); //$NON-NLS-1$
184: jarPackage.setUseSourceFolderHierarchy(getBooleanAttribute(
185: element, "useSourceFolders", false)); //$NON-NLS-1$
186: jarPackage.setDescriptionLocation(Path
187: .fromPortableString(element
188: .getAttribute("descriptionLocation"))); //$NON-NLS-1$
189: jarPackage.setBuildIfNeeded(getBooleanAttribute(element,
190: "buildIfNeeded", jarPackage.isBuildingIfNeeded())); //$NON-NLS-1$
191: jarPackage.setIncludeDirectoryEntries(getBooleanAttribute(
192: element, "includeDirectoryEntries", false)); //$NON-NLS-1$
193: jarPackage.setRefactoringAware(getBooleanAttribute(element,
194: "storeRefactorings", false)); //$NON-NLS-1$
195: }
196: }
197:
198: private void xmlReadRefactoring(JarPackageData jarPackage,
199: Element element) throws java.io.IOException {
200: if (element.getNodeName().equals("storedRefactorings")) { //$NON-NLS-1$
201: jarPackage
202: .setExportStructuralOnly(getBooleanAttribute(
203: element,
204: "structuralOnly", jarPackage.isExportStructuralOnly())); //$NON-NLS-1$
205: jarPackage
206: .setDeprecationAware(getBooleanAttribute(
207: element,
208: "deprecationInfo", jarPackage.isDeprecationAware())); //$NON-NLS-1$
209: List elements = new ArrayList();
210: int count = 1;
211: String value = element.getAttribute("project" + count); //$NON-NLS-1$
212: while (value != null && !"".equals(value)) { //$NON-NLS-1$
213: final IProject project = ResourcesPlugin.getWorkspace()
214: .getRoot().getProject(value);
215: if (project.exists())
216: elements.add(project);
217: count++;
218: value = element.getAttribute("project" + count); //$NON-NLS-1$
219: }
220: jarPackage.setRefactoringProjects((IProject[]) elements
221: .toArray(new IProject[elements.size()]));
222: elements.clear();
223: count = 1;
224: IRefactoringHistoryService service = RefactoringCore
225: .getHistoryService();
226: try {
227: service.connect();
228: value = element.getAttribute("refactoring" + count); //$NON-NLS-1$
229: while (value != null && !"".equals(value)) { //$NON-NLS-1$
230: final ByteArrayInputStream stream = new ByteArrayInputStream(
231: value.getBytes("UTF-8")); //$NON-NLS-1$
232: try {
233: final RefactoringHistory history = service
234: .readRefactoringHistory(stream,
235: RefactoringDescriptor.NONE);
236: if (history != null) {
237: final RefactoringDescriptorProxy[] descriptors = history
238: .getDescriptors();
239: if (descriptors.length > 0) {
240: for (int index = 0; index < descriptors.length; index++) {
241: elements.add(descriptors[index]);
242: }
243: }
244: }
245: } catch (CoreException exception) {
246: JavaPlugin.log(exception);
247: }
248: count++;
249: value = element.getAttribute("refactoring" + count); //$NON-NLS-1$
250: }
251: } finally {
252: service.disconnect();
253: }
254: jarPackage
255: .setRefactoringDescriptors((RefactoringDescriptorProxy[]) elements
256: .toArray(new RefactoringDescriptorProxy[elements
257: .size()]));
258: }
259: }
260:
261: private void xmlReadManifest(JarPackageData jarPackage,
262: Element element) throws java.io.IOException {
263: if (element.getNodeName().equals("manifest")) { //$NON-NLS-1$
264: jarPackage.setManifestVersion(element
265: .getAttribute("manifestVersion")); //$NON-NLS-1$
266: jarPackage.setUsesManifest(getBooleanAttribute(element,
267: "usesManifest")); //$NON-NLS-1$
268: jarPackage.setReuseManifest(getBooleanAttribute(element,
269: "reuseManifest")); //$NON-NLS-1$
270: jarPackage.setSaveManifest(getBooleanAttribute(element,
271: "saveManifest")); //$NON-NLS-1$
272: jarPackage.setGenerateManifest(getBooleanAttribute(element,
273: "generateManifest")); //$NON-NLS-1$
274: jarPackage.setManifestLocation(Path
275: .fromPortableString(element
276: .getAttribute("manifestLocation"))); //$NON-NLS-1$
277: jarPackage.setManifestMainClass(getMainClass(element));
278: xmlReadSealingInfo(jarPackage, element);
279: }
280: }
281:
282: private void xmlReadSealingInfo(JarPackageData jarPackage,
283: Element element) throws java.io.IOException {
284: /*
285: * Try to find sealing info. Could ask for single child node
286: * but this would stop others from adding more child nodes to
287: * the manifest node.
288: */
289: NodeList sealingElementContainer = element.getChildNodes();
290: for (int j = 0; j < sealingElementContainer.getLength(); j++) {
291: Node sealingNode = sealingElementContainer.item(j);
292: if (sealingNode.getNodeType() == Node.ELEMENT_NODE
293: && sealingNode.getNodeName().equals("sealing")) { //$NON-NLS-1$
294: // Sealing
295: Element sealingElement = (Element) sealingNode;
296: jarPackage.setSealJar(getBooleanAttribute(
297: sealingElement, "sealJar")); //$NON-NLS-1$
298: jarPackage.setPackagesToSeal(getPackages(sealingElement
299: .getElementsByTagName("packagesToSeal"))); //$NON-NLS-1$
300: jarPackage
301: .setPackagesToUnseal(getPackages(sealingElement
302: .getElementsByTagName("packagesToUnSeal"))); //$NON-NLS-1$
303: }
304: }
305: }
306:
307: private void xmlReadSelectedElements(JarPackageData jarPackage,
308: Element element) throws java.io.IOException {
309: if (element.getNodeName().equals("selectedElements")) { //$NON-NLS-1$
310: jarPackage.setExportClassFiles(getBooleanAttribute(element,
311: "exportClassFiles")); //$NON-NLS-1$
312: jarPackage.setExportOutputFolders(getBooleanAttribute(
313: element, "exportOutputFolder", false)); //$NON-NLS-1$
314: jarPackage.setExportJavaFiles(getBooleanAttribute(element,
315: "exportJavaFiles")); //$NON-NLS-1$
316: NodeList selectedElements = element.getChildNodes();
317: Set elementsToExport = new HashSet(selectedElements
318: .getLength());
319: for (int j = 0; j < selectedElements.getLength(); j++) {
320: Node selectedNode = selectedElements.item(j);
321: if (selectedNode.getNodeType() != Node.ELEMENT_NODE)
322: continue;
323: Element selectedElement = (Element) selectedNode;
324: if (selectedElement.getNodeName().equals("file")) //$NON-NLS-1$
325: addFile(elementsToExport, selectedElement);
326: else if (selectedElement.getNodeName().equals("folder")) //$NON-NLS-1$
327: addFolder(elementsToExport, selectedElement);
328: else if (selectedElement.getNodeName()
329: .equals("project")) //$NON-NLS-1$
330: addProject(elementsToExport, selectedElement);
331: else if (selectedElement.getNodeName().equals(
332: "javaElement")) //$NON-NLS-1$
333: addJavaElement(elementsToExport, selectedElement);
334: // Note: Other file types are not handled by this writer
335: }
336: jarPackage.setElements(elementsToExport.toArray());
337: }
338: }
339:
340: private void xmlReadSelectedProjects(JarPackageData jarPackage,
341: Element element) throws java.io.IOException {
342: if (element.getNodeName().equals("selectedProjects")) { //$NON-NLS-1$
343: NodeList selectedElements = element.getChildNodes();
344: Set selectedProjects = new HashSet(selectedElements
345: .getLength());
346: for (int index = 0; index < selectedElements.getLength(); index++) {
347: Node node = selectedElements.item(index);
348: if (node.getNodeType() != Node.ELEMENT_NODE)
349: continue;
350: Element selectedElement = (Element) node;
351: if (selectedElement.getNodeName().equals("project")) //$NON-NLS-1$
352: addProject(selectedProjects, selectedElement);
353: }
354: jarPackage
355: .setRefactoringProjects((IProject[]) selectedProjects
356: .toArray(new IProject[selectedProjects
357: .size()]));
358: }
359: }
360:
361: protected boolean getBooleanAttribute(Element element, String name,
362: boolean defaultValue) throws IOException {
363: if (element.hasAttribute(name))
364: return getBooleanAttribute(element, name);
365: else
366: return defaultValue;
367: }
368:
369: protected boolean getBooleanAttribute(Element element, String name)
370: throws IOException {
371: String value = element.getAttribute(name);
372: if (value != null && value.equalsIgnoreCase("true")) //$NON-NLS-1$
373: return true;
374: if (value != null && value.equalsIgnoreCase("false")) //$NON-NLS-1$
375: return false;
376: throw new IOException(
377: JarPackagerMessages.JarPackageReader_error_illegalValueForBooleanAttribute);
378: }
379:
380: private void addFile(Set selectedElements, Element element)
381: throws IOException {
382: IPath path = getPath(element);
383: if (path != null) {
384: IFile file = JavaPlugin.getWorkspace().getRoot().getFile(
385: path);
386: if (file != null)
387: selectedElements.add(file);
388: }
389: }
390:
391: private void addFolder(Set selectedElements, Element element)
392: throws IOException {
393: IPath path = getPath(element);
394: if (path != null) {
395: IFolder folder = JavaPlugin.getWorkspace().getRoot()
396: .getFolder(path);
397: if (folder != null)
398: selectedElements.add(folder);
399: }
400: }
401:
402: private void addProject(Set selectedElements, Element element)
403: throws IOException {
404: String name = element.getAttribute("name"); //$NON-NLS-1$
405: if (name.length() == 0)
406: throw new IOException(
407: JarPackagerMessages.JarPackageReader_error_tagNameNotFound);
408: IProject project = JavaPlugin.getWorkspace().getRoot()
409: .getProject(name);
410: if (project != null)
411: selectedElements.add(project);
412: }
413:
414: private IPath getPath(Element element) throws IOException {
415: String pathString = element.getAttribute("path"); //$NON-NLS-1$
416: if (pathString.length() == 0)
417: throw new IOException(
418: JarPackagerMessages.JarPackageReader_error_tagPathNotFound);
419: return Path.fromPortableString(element.getAttribute("path")); //$NON-NLS-1$
420: }
421:
422: private void addJavaElement(Set selectedElements, Element element)
423: throws IOException {
424: String handleId = element.getAttribute("handleIdentifier"); //$NON-NLS-1$
425: if (handleId.length() == 0)
426: throw new IOException(
427: JarPackagerMessages.JarPackageReader_error_tagHandleIdentifierNotFoundOrEmpty);
428: IJavaElement je = JavaCore.create(handleId);
429: if (je == null)
430: addWarning(
431: JarPackagerMessages.JarPackageReader_warning_javaElementDoesNotExist,
432: null);
433: else
434: selectedElements.add(je);
435: }
436:
437: private IPackageFragment[] getPackages(NodeList list)
438: throws IOException {
439: if (list.getLength() > 1)
440: throw new IOException(
441: Messages
442: .format(
443: JarPackagerMessages.JarPackageReader_error_duplicateTag,
444: list.item(0).getNodeName()));
445: if (list.getLength() == 0)
446: return null; // optional entry is not present
447: NodeList packageNodes = list.item(0).getChildNodes();
448: List packages = new ArrayList(packageNodes.getLength());
449: for (int i = 0; i < packageNodes.getLength(); i++) {
450: Node packageNode = packageNodes.item(i);
451: if (packageNode.getNodeType() == Node.ELEMENT_NODE
452: && packageNode.getNodeName().equals("package")) { //$NON-NLS-1$
453: String handleId = ((Element) packageNode)
454: .getAttribute("handleIdentifier"); //$NON-NLS-1$
455: if (handleId.equals("")) //$NON-NLS-1$
456: throw new IOException(
457: JarPackagerMessages.JarPackageReader_error_tagHandleIdentifierNotFoundOrEmpty);
458: IJavaElement je = JavaCore.create(handleId);
459: if (je != null
460: && je.getElementType() == IJavaElement.PACKAGE_FRAGMENT)
461: packages.add(je);
462: else
463: addWarning(
464: JarPackagerMessages.JarPackageReader_warning_javaElementDoesNotExist,
465: null);
466: }
467: }
468: return (IPackageFragment[]) packages
469: .toArray(new IPackageFragment[packages.size()]);
470: }
471:
472: private IType getMainClass(Element element) {
473: String handleId = element
474: .getAttribute("mainClassHandleIdentifier"); //$NON-NLS-1$
475: if (handleId.equals("")) //$NON-NLS-1$
476: return null; // Main-Class entry is optional or can be empty
477: IJavaElement je = JavaCore.create(handleId);
478: if (je != null && je.getElementType() == IJavaElement.TYPE)
479: return (IType) je;
480: addWarning(
481: JarPackagerMessages.JarPackageReader_warning_mainClassDoesNotExist,
482: null);
483: return null;
484: }
485:
486: /**
487: * Returns the status of the reader.
488: * If there were any errors, the result is a status object containing
489: * individual status objects for each error.
490: * If there were no errors, the result is a status object with error code <code>OK</code>.
491: *
492: * @return the status of this operation
493: */
494: public IStatus getStatus() {
495: if (fWarnings.getChildren().length == 0)
496: return new Status(IStatus.OK, JavaPlugin.getPluginId(), 0,
497: "", null); //$NON-NLS-1$
498: else
499: return fWarnings;
500: }
501:
502: /**
503: * Adds a new warning to the list with the passed information.
504: * Normally the export operation continues after a warning.
505: * @param message the message
506: * @param error the throwable that caused the warning, or <code>null</code>
507: */
508: protected void addWarning(String message, Throwable error) {
509: fWarnings.add(new Status(IStatus.WARNING, JavaPlugin
510: .getPluginId(), 0, message, error));
511: }
512: }
|