Index: model/org/eclipse/jdt/internal/core/ClasspathEntry.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClasspathEntry.java,v retrieving revision 1.81 diff -u -r1.81 ClasspathEntry.java --- model/org/eclipse/jdt/internal/core/ClasspathEntry.java 1 Jun 2005 18:56:14 -0000 1.81 +++ model/org/eclipse/jdt/internal/core/ClasspathEntry.java 31 Aug 2005 13:36:29 -0000 @@ -10,11 +10,16 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core; +import java.io.CharArrayWriter; import java.io.File; +import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import org.apache.crimson.tree.XmlWritable; +import org.apache.crimson.tree.XmlWriteContext; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; @@ -39,7 +44,9 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; +import org.w3c.dom.DOMException; import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -119,6 +126,12 @@ private String rootID; private AccessRuleSet accessRuleSet; + + static class UnknownXmlElements { + String[] attributes; + ArrayList children; + } + /* * Default inclusion pattern set */ @@ -278,10 +291,7 @@ return result; } - static IClasspathAttribute[] decodeExtraAttributes(Element element) { - Node extra = element.getElementsByTagName(TAG_ATTRIBUTES).item(0); - if (extra == null) return NO_EXTRA_ATTRIBUTES; - NodeList attributes = element.getElementsByTagName(TAG_ATTRIBUTE); + static IClasspathAttribute[] decodeExtraAttributes(NodeList attributes) { if (attributes == null) return NO_EXTRA_ATTRIBUTES; int length = attributes.getLength(); if (length == 0) return NO_EXTRA_ATTRIBUTES; @@ -303,31 +313,30 @@ return result; } - static IAccessRule[] decodeAccessRules(Element element) { - Node accessRules = element.getElementsByTagName(TAG_ACCESS_RULES).item(0); - if (accessRules == null || accessRules.getNodeType() != Node.ELEMENT_NODE) return null; - NodeList list = ((Element) accessRules).getElementsByTagName(TAG_ACCESS_RULE); + static IAccessRule[] decodeAccessRules(NodeList list) { + if (list == null) return null; int length = list.getLength(); if (length == 0) return null; IAccessRule[] result = new IAccessRule[length]; int index = 0; for (int i = 0; i < length; i++) { Node accessRule = list.item(i); - if (accessRule == null || accessRule.getNodeType() != Node.ELEMENT_NODE) return null; - Element elementAccessRule = (Element) accessRule; - String pattern = elementAccessRule.getAttribute(TAG_PATTERN); - if (pattern == null) continue; - String tagKind = elementAccessRule.getAttribute(TAG_KIND); - int kind; - if (TAG_ACCESSIBLE.equals(tagKind)) - kind = IAccessRule.K_ACCESSIBLE; - else if (TAG_NON_ACCESSIBLE.equals(tagKind)) - kind = IAccessRule.K_NON_ACCESSIBLE; - else if (TAG_DISCOURAGED.equals(tagKind)) - kind = IAccessRule.K_DISCOURAGED; - else - continue; - result[index++] = new ClasspathAccessRule(new Path(pattern), kind); + if (accessRule.getNodeType() == Node.ELEMENT_NODE) { + Element elementAccessRule = (Element) accessRule; + String pattern = elementAccessRule.getAttribute(TAG_PATTERN); + if (pattern == null) continue; + String tagKind = elementAccessRule.getAttribute(TAG_KIND); + int kind; + if (TAG_ACCESSIBLE.equals(tagKind)) + kind = IAccessRule.K_ACCESSIBLE; + else if (TAG_NON_ACCESSIBLE.equals(tagKind)) + kind = IAccessRule.K_NON_ACCESSIBLE; + else if (TAG_DISCOURAGED.equals(tagKind)) + kind = IAccessRule.K_DISCOURAGED; + else + continue; + result[index++] = new ClasspathAccessRule(new Path(pattern), kind); + } } if (index != length) System.arraycopy(result, 0, result = new IAccessRule[index], 0, index); @@ -337,8 +346,8 @@ /** * Decode some element tag containing a sequence of patterns into IPath[] */ - private static IPath[] decodePatterns(Element element, String tag) { - String sequence = element.getAttribute(tag); + private static IPath[] decodePatterns(NamedNodeMap nodeMap, String tag) { + String sequence = removeAttribute(tag, nodeMap); if (!sequence.equals("")) { //$NON-NLS-1$ char[][] patterns = CharOperation.splitOn('|', sequence.toCharArray()); int patternCount; @@ -352,6 +361,22 @@ } return null; } + + private static void decodeUnknownChild(Node child, StringBuffer buffer) { + if (child instanceof XmlWritable) { + CharArrayWriter writer = new CharArrayWriter(); + XmlWriteContext context = new XmlWriteContext(writer); + try { + ((XmlWritable) child).writeXml(context); + buffer.append(writer.toString()); + } catch (IOException e) { + buffer.append(""); //$NON-NLS-1$ + } + } else { + buffer.append(""); //$NON-NLS-1$ + } + } + /* * Returns a char based representation of the exclusions patterns full path. */ @@ -389,7 +414,7 @@ /** * Returns the XML encoding of the class path. */ - public void elementEncode(XMLWriter writer, IPath projectPath, boolean indent, boolean newLine) { + public void elementEncode(XMLWriter writer, IPath projectPath, boolean indent, boolean newLine, Map unknownElements) { HashMap parameters = new HashMap(); parameters.put(TAG_KIND, ClasspathEntry.kindToString(this.entryKind)); @@ -433,6 +458,16 @@ parameters.put(TAG_COMBINE_ACCESS_RULES, "false"); //$NON-NLS-1$ + // unknown attributes + UnknownXmlElements unknownXmlElements = unknownElements == null ? null : (UnknownXmlElements) unknownElements.get(this.path); + String[] unknownAttributes; + if (unknownXmlElements != null && (unknownAttributes = unknownXmlElements.attributes) != null) + for (int i = 0, length = unknownAttributes.length; i < length; i+=2) { + String tagName = unknownAttributes[i]; + String tagValue = unknownAttributes[i+1]; + parameters.put(tagName, tagValue); + } + if (this.specificOutputLocation != null) { IPath outputLocation = this.specificOutputLocation.removeFirstSegments(1); outputLocation = outputLocation.makeRelative(); @@ -441,15 +476,25 @@ boolean hasExtraAttributes = this.extraAttributes.length != 0; boolean hasRestrictions = getAccessRuleSet() != null; // access rule set is null if no access rules - writer.printTag(TAG_CLASSPATHENTRY, parameters, indent, newLine, !hasExtraAttributes && !hasRestrictions /*close tag if no extra attributes and no restriction*/); + ArrayList unknownChildren = unknownXmlElements != null ? unknownXmlElements.children : null; + boolean hasUnknownChildren = unknownChildren != null; + writer.printTag( + TAG_CLASSPATHENTRY, + parameters, + indent, + newLine, + !hasExtraAttributes && !hasRestrictions && !hasUnknownChildren/*close tag if no extra attributes, no restriction and no unknown children*/); if (hasExtraAttributes) encodeExtraAttributes(writer, indent, newLine); if (hasRestrictions) encodeAccessRules(writer, indent, newLine); + + if (hasUnknownChildren) + encodeUnknownChildren(writer, indent, newLine, unknownChildren); - if (hasExtraAttributes || hasRestrictions) + if (hasExtraAttributes || hasRestrictions || hasUnknownChildren) writer.endTag(TAG_CLASSPATHENTRY, indent); } @@ -496,11 +541,21 @@ } - public static IClasspathEntry elementDecode(Element element, IJavaProject project) { + private void encodeUnknownChildren(XMLWriter writer, boolean indent, boolean newLine, ArrayList unknownChildren) { + for (int i = 0, length = unknownChildren.size(); i < length; i++) { + String child = (String) unknownChildren.get(i); + writer.printString(child, indent, newLine); + } + } + + public static IClasspathEntry elementDecode(Element element, IJavaProject project, Map unknownElements) { IPath projectPath = project.getProject().getFullPath(); - String kindAttr = element.getAttribute(TAG_KIND); - String pathAttr = element.getAttribute(TAG_PATH); + NamedNodeMap attributes = element.getAttributes(); + NodeList children = element.getChildNodes(); + boolean[] foundChildren = new boolean[children.getLength()]; + String kindAttr = removeAttribute(TAG_KIND, attributes); + String pathAttr = removeAttribute(TAG_PATH, attributes); // ensure path is absolute IPath path = new Path(pathAttr); @@ -511,29 +566,30 @@ // source attachment info (optional) IPath sourceAttachmentPath = element.hasAttribute(TAG_SOURCEPATH) - ? new Path(element.getAttribute(TAG_SOURCEPATH)) + ? new Path(removeAttribute(TAG_SOURCEPATH, attributes)) : null; if (kind != IClasspathEntry.CPE_VARIABLE && sourceAttachmentPath != null && !sourceAttachmentPath.isAbsolute()) { sourceAttachmentPath = projectPath.append(sourceAttachmentPath); } IPath sourceAttachmentRootPath = element.hasAttribute(TAG_ROOTPATH) - ? new Path(element.getAttribute(TAG_ROOTPATH)) + ? new Path(removeAttribute(TAG_ROOTPATH, attributes)) : null; // exported flag (optional) - boolean isExported = element.getAttribute(TAG_EXPORTED).equals("true"); //$NON-NLS-1$ + boolean isExported = removeAttribute(TAG_EXPORTED, attributes).equals("true"); //$NON-NLS-1$ // inclusion patterns (optional) - IPath[] inclusionPatterns = decodePatterns(element, TAG_INCLUDING); + IPath[] inclusionPatterns = decodePatterns(attributes, TAG_INCLUDING); if (inclusionPatterns == null) inclusionPatterns = INCLUDE_ALL; // exclusion patterns (optional) - IPath[] exclusionPatterns = decodePatterns(element, TAG_EXCLUDING); + IPath[] exclusionPatterns = decodePatterns(attributes, TAG_EXCLUDING); if (exclusionPatterns == null) exclusionPatterns = EXCLUDE_NONE; // access rules (optional) - IAccessRule[] accessRules = decodeAccessRules(element); + NodeList attributeList = getChildAttributes(TAG_ACCESS_RULES, children, foundChildren); + IAccessRule[] accessRules = decodeAccessRules(attributeList); // backward compatibility if (accessRules == null) { @@ -541,25 +597,62 @@ } // combine access rules (optional) - boolean combineAccessRestrictions = !element.getAttribute(TAG_COMBINE_ACCESS_RULES).equals("false"); //$NON-NLS-1$ + boolean combineAccessRestrictions = !removeAttribute(TAG_COMBINE_ACCESS_RULES, attributes).equals("false"); //$NON-NLS-1$ // extra attributes (optional) - IClasspathAttribute[] extraAttributes = decodeExtraAttributes(element); + attributeList = getChildAttributes(TAG_ATTRIBUTES, children, foundChildren); + IClasspathAttribute[] extraAttributes = decodeExtraAttributes(attributeList); // custom output location - IPath outputLocation = element.hasAttribute(TAG_OUTPUT) ? projectPath.append(element.getAttribute(TAG_OUTPUT)) : null; + IPath outputLocation = element.hasAttribute(TAG_OUTPUT) ? projectPath.append(removeAttribute(TAG_OUTPUT, attributes)) : null; + + String[] unknownAttributes = null; + ArrayList unknownChildren = null; + + if (unknownElements != null) { + // unknown attributes + int unknownAttributeLength = attributes.getLength(); + if (unknownAttributeLength != 0) { + unknownAttributes = new String[unknownAttributeLength*2]; + for (int i = 0; i < unknownAttributeLength; i++) { + Node attribute = attributes.item(i); + unknownAttributes[i*2] = attribute.getNodeName(); + unknownAttributes[i*2 + 1] = attribute.getNodeValue(); + } + } + + // unknown children + for (int i = 0, length = foundChildren.length; i < length; i++) { + if (!foundChildren[i]) { + Node node = children.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) continue; + if (unknownChildren == null) + unknownChildren = new ArrayList(); + StringBuffer buffer = new StringBuffer(); + decodeUnknownChild(node, buffer); + unknownChildren.add(buffer.toString()); + } + } + } // recreate the CP entry IClasspathEntry entry = null; switch (kind) { case IClasspathEntry.CPE_PROJECT : - entry = JavaCore.newProjectEntry( - path, - accessRules, - combineAccessRestrictions, - extraAttributes, - isExported); + entry = new ClasspathEntry( + IPackageFragmentRoot.K_SOURCE, + IClasspathEntry.CPE_PROJECT, + path, + ClasspathEntry.INCLUDE_ALL, // inclusion patterns + ClasspathEntry.EXCLUDE_NONE, // exclusion patterns + null, // source attachment + null, // source attachment root + null, // specific output folder + isExported, + accessRules, + combineAccessRestrictions, + extraAttributes); break; case IClasspathEntry.CPE_LIBRARY : entry = JavaCore.newLibraryEntry( @@ -574,7 +667,7 @@ // must be an entry in this project or specify another project String projSegment = path.segment(0); if (projSegment != null && projSegment.equals(project.getElementName())) { // this project - return JavaCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation, extraAttributes); + entry = JavaCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation, extraAttributes); } else { if (path.segmentCount() == 1) { // another project @@ -586,7 +679,7 @@ isExported); } else { // an invalid source folder - return JavaCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation, extraAttributes); + entry = JavaCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation, extraAttributes); } } break; @@ -608,7 +701,7 @@ break; case ClasspathEntry.K_OUTPUT : if (!path.isAbsolute()) return null; - return new ClasspathEntry( + entry = new ClasspathEntry( ClasspathEntry.K_OUTPUT, IClasspathEntry.CPE_LIBRARY, path, @@ -621,11 +714,49 @@ null, // no access rules false, // no accessible files to combine NO_EXTRA_ATTRIBUTES); + break; default : throw new Assert.AssertionFailedException(Messages.bind(Messages.classpath_unknownKind, kindAttr)); } + + if (unknownAttributes != null || unknownChildren != null) { + UnknownXmlElements unknownXmlElements = new UnknownXmlElements(); + unknownXmlElements.attributes = unknownAttributes; + unknownXmlElements.children = unknownChildren; + unknownElements.put(path, unknownXmlElements); + } + return entry; } + + public static NodeList getChildAttributes(String childName, NodeList children, boolean[] foundChildren) { + for (int i = 0, length = foundChildren.length; i < length; i++) { + Node node = children.item(i); + if (childName.equals(node.getNodeName())) { + foundChildren[i] = true; + return node.getChildNodes(); + } + } + return null; + } + + + private static String removeAttribute(String nodeName, NamedNodeMap nodeMap) { + Node node = removeNode(nodeName, nodeMap); + if (node == null) + return ""; // //$NON-NLS-1$ + return node.getNodeValue(); + } + + private static Node removeNode(String nodeName, NamedNodeMap nodeMap) { + try { + return nodeMap.removeNamedItem(nodeName); + } catch (DOMException e) { + if (e.code != DOMException.NOT_FOUND_ERR) + throw e; + return null; + } + } /** * Encode some patterns into XML parameter tag Index: model/org/eclipse/jdt/internal/core/JavaModelManager.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java,v retrieving revision 1.285.2.1 diff -u -r1.285.2.1 JavaModelManager.java --- model/org/eclipse/jdt/internal/core/JavaModelManager.java 17 Aug 2005 13:50:43 -0000 1.285.2.1 +++ model/org/eclipse/jdt/internal/core/JavaModelManager.java 31 Aug 2005 13:36:31 -0000 @@ -2278,7 +2278,8 @@ containerString = ((JavaProject)project).encodeClasspath( entries, null, - false); + false, + null/*not interested in unknown elements*/); } } catch(JavaModelException e){ // could not encode entry: will not persist Index: model/org/eclipse/jdt/internal/core/JavaProject.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java,v retrieving revision 1.347 diff -u -r1.347 JavaProject.java --- model/org/eclipse/jdt/internal/core/JavaProject.java 2 Jun 2005 13:09:05 -0000 1.347 +++ model/org/eclipse/jdt/internal/core/JavaProject.java 31 Aug 2005 13:36:32 -0000 @@ -796,6 +796,13 @@ * Reads and decode an XML classpath string */ protected IClasspathEntry[] decodeClasspath(String xmlClasspath, boolean createMarker, boolean logProblems) { + return decodeClasspath(xmlClasspath, createMarker, logProblems, null/*not interested in unknown elements*/); + } + + /** + * Reads and decode an XML classpath string + */ + protected IClasspathEntry[] decodeClasspath(String xmlClasspath, boolean createMarker, boolean logProblems, Map unknownElements) { ArrayList paths = new ArrayList(); IClasspathEntry defaultOutput = null; @@ -825,7 +832,7 @@ for (int i = 0; i < length; ++i) { Node node = list.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { - IClasspathEntry entry = ClasspathEntry.elementDecode((Element)node, this); + IClasspathEntry entry = ClasspathEntry.elementDecode((Element)node, this, unknownElements); if (entry != null){ if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) { defaultOutput = entry; // separate output @@ -901,7 +908,7 @@ /** * Returns the XML String encoding of the class path. */ - protected String encodeClasspath(IClasspathEntry[] classpath, IPath outputLocation, boolean indent) throws JavaModelException { + protected String encodeClasspath(IClasspathEntry[] classpath, IPath outputLocation, boolean indent, Map unknownElements) throws JavaModelException { try { ByteArrayOutputStream s = new ByteArrayOutputStream(); OutputStreamWriter writer = new OutputStreamWriter(s, "UTF8"); //$NON-NLS-1$ @@ -909,7 +916,7 @@ xmlWriter.startTag(ClasspathEntry.TAG_CLASSPATH, indent); for (int i = 0; i < classpath.length; ++i) { - ((ClasspathEntry)classpath[i]).elementEncode(xmlWriter, this.project.getFullPath(), indent, true); + ((ClasspathEntry)classpath[i]).elementEncode(xmlWriter, this.project.getFullPath(), indent, true, unknownElements); } if (outputLocation != null) { @@ -2537,6 +2544,10 @@ * Returns INVALID_CLASSPATH if it has a format problem. */ protected IClasspathEntry[] readClasspathFile(boolean createMarker, boolean logProblems) { + return readClasspathFile(createMarker, logProblems, null/*not interested in unknown elements*/); + } + + protected IClasspathEntry[] readClasspathFile(boolean createMarker, boolean logProblems, Map unknownElements) { try { String xmlClasspath = getSharedProperty(CLASSPATH_FILENAME); @@ -2548,7 +2559,7 @@ } return null; } - return decodeClasspath(xmlClasspath, createMarker, logProblems); + return decodeClasspath(xmlClasspath, createMarker, logProblems, unknownElements); } catch(CoreException e) { // file does not exist (or not accessible) if (createMarker && this.project.isAccessible()) { @@ -2653,7 +2664,8 @@ if (!this.project.isAccessible()) return false; - IClasspathEntry[] fileEntries = readClasspathFile(false /*don't create markers*/, false/*don't log problems*/); + Map unknownElements = new HashMap(); + IClasspathEntry[] fileEntries = readClasspathFile(false /*don't create markers*/, false/*don't log problems*/, unknownElements); if (fileEntries != null && isClasspathEqualsTo(newClasspath, newOutputLocation, fileEntries)) { // no need to save it, it is the same return false; @@ -2661,7 +2673,7 @@ // actual file saving try { - setSharedProperty(CLASSPATH_FILENAME, encodeClasspath(newClasspath, newOutputLocation, true)); + setSharedProperty(CLASSPATH_FILENAME, encodeClasspath(newClasspath, newOutputLocation, true, unknownElements)); return true; } catch (CoreException e) { throw new JavaModelException(e); Index: model/org/eclipse/jdt/internal/core/UserLibrary.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/UserLibrary.java,v retrieving revision 1.6 diff -u -r1.6 UserLibrary.java --- model/org/eclipse/jdt/internal/core/UserLibrary.java 23 May 2005 15:11:35 -0000 1.6 +++ model/org/eclipse/jdt/internal/core/UserLibrary.java 31 Aug 2005 13:36:33 -0000 @@ -177,8 +177,12 @@ String path = element.getAttribute(TAG_PATH); IPath sourceAttach= element.hasAttribute(TAG_SOURCEATTACHMENT) ? new Path(element.getAttribute(TAG_SOURCEATTACHMENT)) : null; IPath sourceAttachRoot= element.hasAttribute(TAG_SOURCEATTACHMENTROOT) ? new Path(element.getAttribute(TAG_SOURCEATTACHMENTROOT)) : null; - IClasspathAttribute[] extraAttributes = ClasspathEntry.decodeExtraAttributes(element); - IAccessRule[] accessRules = ClasspathEntry.decodeAccessRules(element); + NodeList children = element.getElementsByTagName("*"); //$NON-NLS-1$ + boolean[] foundChildren = new boolean[children.getLength()]; + NodeList attributeList = ClasspathEntry.getChildAttributes(ClasspathEntry.TAG_ATTRIBUTES, children, foundChildren); + IClasspathAttribute[] extraAttributes = ClasspathEntry.decodeExtraAttributes(attributeList); + attributeList = ClasspathEntry.getChildAttributes(ClasspathEntry.TAG_ACCESS_RULES, children, foundChildren); + IAccessRule[] accessRules = ClasspathEntry.decodeAccessRules(attributeList); IClasspathEntry entry = JavaCore.newLibraryEntry(new Path(path), sourceAttach, sourceAttachRoot, accessRules, extraAttributes, false/*not exported*/); res.add(entry); } Index: model/org/eclipse/jdt/internal/core/XMLWriter.java =================================================================== RCS file: /home/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/XMLWriter.java,v retrieving revision 1.7 diff -u -r1.7 XMLWriter.java --- model/org/eclipse/jdt/internal/core/XMLWriter.java 23 May 2005 15:11:35 -0000 1.7 +++ model/org/eclipse/jdt/internal/core/XMLWriter.java 31 Aug 2005 13:36:33 -0000 @@ -92,16 +92,19 @@ } else { sb.append(">"); //$NON-NLS-1$ } + printString(sb.toString(), insertTab, insertNewLine); + if (parameters != null && !closeTag) + this.tab++; + + } + public void printString(String string, boolean insertTab, boolean insertNewLine) { if (insertTab) { printTabulation(); } - print(sb.toString()); + print(string); if (insertNewLine) { print(this.lineSeparator); } - if (parameters != null && !closeTag) - this.tab++; - } public void startTag(String name, boolean insertTab) { printTag(name, null/*no parameters*/, insertTab, true/*insert new line*/, false/*don't close tag*/);