diff --git a/src/java/org/apache/ivy/core/module/descriptor/DefaultModuleDescriptor.java b/src/java/org/apache/ivy/core/module/descriptor/DefaultModuleDescriptor.java index 9103fa8f..7e422882 100644 --- a/src/java/org/apache/ivy/core/module/descriptor/DefaultModuleDescriptor.java +++ b/src/java/org/apache/ivy/core/module/descriptor/DefaultModuleDescriptor.java @@ -194,6 +194,7 @@ public class DefaultModuleDescriptor implements ModuleDescriptor { nmd.lastModified = md.getLastModified(); nmd.extraAttributesNamespaces = md.getExtraAttributesNamespaces(); nmd.extraInfo = md.getExtraInfo(); + nmd.extraInfos = md.getExtraInfos(); nmd.namespace = ns; return nmd; @@ -256,6 +257,8 @@ public class DefaultModuleDescriptor implements ModuleDescriptor { private Map/* */extraInfo = new HashMap(); + private List extraInfos = new ArrayList(); + public DefaultModuleDescriptor(ModuleRevisionId id, String status, Date pubDate) { this(id, status, pubDate, false); } @@ -840,4 +843,12 @@ public class DefaultModuleDescriptor implements ModuleDescriptor { public Map getExtraInfo() { return extraInfo; } + + public List getExtraInfos() { + return extraInfos; + } + + public void addExtraInfo(ExtraInfoHolder extraInfo) { + extraInfos.add(extraInfo); + } } diff --git a/src/java/org/apache/ivy/core/module/descriptor/ExtraInfoHolder.java b/src/java/org/apache/ivy/core/module/descriptor/ExtraInfoHolder.java new file mode 100644 index 00000000..4c16b670 --- /dev/null +++ b/src/java/org/apache/ivy/core/module/descriptor/ExtraInfoHolder.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivy.core.module.descriptor; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class ExtraInfoHolder { + + private String name; + private Map attributes = new LinkedHashMap(); + private String content; + private List nestedExtraInfoHolder = new ArrayList(); + + public ExtraInfoHolder() { + + } + + public ExtraInfoHolder(String name, String content) { + this.name = name; + this.content = content; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public List getNestedExtraInfoHolder() { + return nestedExtraInfoHolder; + } + + public void setNestedExtraInfoHolder(List nestedExtraInfoHolder) { + this.nestedExtraInfoHolder = nestedExtraInfoHolder; + } + +} diff --git a/src/java/org/apache/ivy/core/module/descriptor/ModuleDescriptor.java b/src/java/org/apache/ivy/core/module/descriptor/ModuleDescriptor.java index 3b961f27..3965b6e5 100644 --- a/src/java/org/apache/ivy/core/module/descriptor/ModuleDescriptor.java +++ b/src/java/org/apache/ivy/core/module/descriptor/ModuleDescriptor.java @@ -21,8 +21,10 @@ import java.io.File; import java.io.IOException; import java.text.ParseException; import java.util.Date; +import java.util.List; import java.util.Map; +import org.apache.ivy.core.module.descriptor.ExtraInfoHolder; import org.apache.ivy.core.module.id.ArtifactId; import org.apache.ivy.core.module.id.ModuleId; import org.apache.ivy.core.module.id.ModuleRevisionId; @@ -263,5 +265,14 @@ public interface ModuleDescriptor * The key is the name of the tag, the value is its content. * @return */ + @Deprecated Map/**/ getExtraInfo(); + + + /** + * Returns a list of extras infos (tag name, attributes and content). + * All the tags except the description are given. + * @return + */ + List getExtraInfos(); } diff --git a/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParser.java b/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParser.java index 1a1f15ff..43465539 100644 --- a/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParser.java +++ b/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParser.java @@ -26,7 +26,9 @@ import java.net.URISyntaxException; import java.net.URL; import java.text.ParseException; import java.util.Arrays; +import java.util.Deque; import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -47,6 +49,7 @@ import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor; import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor; import org.apache.ivy.core.module.descriptor.DependencyDescriptor; import org.apache.ivy.core.module.descriptor.ExcludeRule; +import org.apache.ivy.core.module.descriptor.ExtraInfoHolder; import org.apache.ivy.core.module.descriptor.IncludeRule; import org.apache.ivy.core.module.descriptor.License; import org.apache.ivy.core.module.descriptor.MDArtifact; @@ -227,6 +230,7 @@ public class XmlModuleDescriptorParser extends AbstractModuleDescriptorParser { private StringBuffer buffer; private String descriptorVersion; private String[] publicationsDefaultConf; + private Deque extraInfoStack = new LinkedList(); public Parser(ModuleDescriptorParser parser, ParserSettings ivySettings) { super(parser); @@ -313,9 +317,17 @@ public class XmlModuleDescriptorParser extends AbstractModuleDescriptorParser { // nothing to do, we don't store this } else if (state == State.INFO && "repository".equals(qName)) { // nothing to do, we don't store this - } else if (state == State.INFO && isOtherNamespace(qName)) { + } else if (state == State.EXTRA_INFO || state == State.INFO + && isOtherNamespace(qName)) { buffer = new StringBuffer(); state = State.EXTRA_INFO; + ExtraInfoHolder extraInfo = new ExtraInfoHolder(); + extraInfo.setName(qName); + for (int i = 0; i < attributes.getLength(); i++) { + extraInfo.getAttributes().put(attributes.getQName(i), + attributes.getValue(i)); + } + extraInfoStack.push(extraInfo); } else if ("configurations".equals(qName)) { configurationStarted(attributes); } else if ("publications".equals(qName)) { @@ -1194,9 +1206,18 @@ public class XmlModuleDescriptorParser extends AbstractModuleDescriptorParser { buffer = null; state = State.INFO; } else if (state == State.EXTRA_INFO) { - getMd().addExtraInfo(qName, buffer == null ? "" : buffer.toString()); + String content = buffer == null ? "" : buffer.toString(); buffer = null; - state = State.INFO; + getMd().addExtraInfo(qName, content); + ExtraInfoHolder extraInfo = extraInfoStack.pop(); + extraInfo.setContent(content); + if (extraInfoStack.isEmpty()) { + getMd().addExtraInfo(extraInfo); + state = State.INFO; + } else { + ExtraInfoHolder parentHolder = extraInfoStack.peek(); + parentHolder.getNestedExtraInfoHolder().add(extraInfo); + } } else if (state == State.DESCRIPTION) { if (buffer.toString().endsWith("<" + qName + ">")) { buffer.deleteCharAt(buffer.length() - 1); diff --git a/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriter.java b/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriter.java index 2513e0c2..ecc71bda 100644 --- a/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriter.java +++ b/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriter.java @@ -36,6 +36,7 @@ import org.apache.ivy.core.module.descriptor.DependencyDescriptor; import org.apache.ivy.core.module.descriptor.DependencyDescriptorMediator; import org.apache.ivy.core.module.descriptor.ExcludeRule; import org.apache.ivy.core.module.descriptor.ExtendsDescriptor; +import org.apache.ivy.core.module.descriptor.ExtraInfoHolder; import org.apache.ivy.core.module.descriptor.IncludeRule; import org.apache.ivy.core.module.descriptor.License; import org.apache.ivy.core.module.descriptor.ModuleDescriptor; @@ -507,19 +508,8 @@ public final class XmlModuleDescriptorWriter { out.println(" />"); } } - for (Iterator it = md.getExtraInfo().entrySet().iterator(); it.hasNext();) { - Map.Entry extraDescr = (Map.Entry) it.next(); - if (extraDescr.getValue() == null - || ((String) extraDescr.getValue()).length() == 0) { - continue; - } - out.print("\t\t<"); - out.print(extraDescr.getKey()); - out.print(">"); - out.print(XMLHelper.escape((String) extraDescr.getValue())); - out.print(""); + for (ExtraInfoHolder extraInfo : md.getExtraInfos()) { + printExtraInfoElement(out, extraInfo, 2); } out.println("\t"); } else { @@ -527,9 +517,50 @@ public final class XmlModuleDescriptorWriter { } } + + private static void printExtraInfoElement(PrintWriter out, ExtraInfoHolder extraInfo, int indent) { + for (int i = 1; i <= indent; i++) { + out.print("\t"); + } + out.print("<"); + out.print(extraInfo.getName()); + for (Entry entry : extraInfo.getAttributes().entrySet()) { + out.print(" "); + out.print(entry.getKey()); + out.print("="); + out.print("\""); + out.print(entry.getValue()); + out.print("\""); + } + boolean requireClosingTag = false; + if (extraInfo.getContent() != null && extraInfo.getContent().trim().length() > 0) { + out.print(">"); + out.print(XMLHelper.escape(extraInfo.getContent())); + requireClosingTag = true; + } + if (!extraInfo.getNestedExtraInfoHolder().isEmpty()) { + out.println(">"); + for (ExtraInfoHolder nestedElement : extraInfo.getNestedExtraInfoHolder()) { + printExtraInfoElement(out, nestedElement, indent + 1); + } + requireClosingTag = true; + // prepare indentation for closing tag + for (int i = 1; i <= indent; i++) { + out.print("\t"); + } + } + if (requireClosingTag) { + out.print(""); + } else { + out.println("/>"); + } + } private static boolean requireInnerInfoElement(ModuleDescriptor md) { return md.getExtraInfo().size() > 0 + || md.getExtraInfos().size() > 0 || md.getHomePage() != null || (md.getDescription() != null && md.getDescription().trim().length() > 0) || md.getLicenses().length > 0 diff --git a/test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParserTest.java b/test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParserTest.java index b5d2cec0..96d3563b 100644 --- a/test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParserTest.java +++ b/test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParserTest.java @@ -32,6 +32,7 @@ import org.apache.ivy.core.module.descriptor.Configuration; import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor; import org.apache.ivy.core.module.descriptor.DependencyDescriptor; import org.apache.ivy.core.module.descriptor.ExcludeRule; +import org.apache.ivy.core.module.descriptor.ExtraInfoHolder; import org.apache.ivy.core.module.descriptor.License; import org.apache.ivy.core.module.descriptor.ModuleDescriptor; import org.apache.ivy.core.module.descriptor.Configuration.Visibility; @@ -490,6 +491,60 @@ public class XmlModuleDescriptorParserTest extends AbstractModuleDescriptorParse assertEquals("56576", md.getExtraInfo().get("e:someExtra")); } + public void testExtraInfos() throws Exception { + ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings, + getClass().getResource("test-extrainfo.xml"), true); + assertNotNull(md); + assertEquals(2, md.getExtraInfo().size()); + assertEquals("56576", md.getExtraInfo().get("e:someExtra")); + assertEquals(2, md.getExtraInfos().size()); + ExtraInfoHolder firstExtraInfoElement = md.getExtraInfos().get(0); + assertEquals("e:someExtra", firstExtraInfoElement.getName()); + assertEquals("56576", firstExtraInfoElement.getContent()); + assertEquals(0, firstExtraInfoElement.getAttributes().size()); + assertEquals(0, firstExtraInfoElement.getNestedExtraInfoHolder().size()); + ExtraInfoHolder secondExtraInfoElement = md.getExtraInfos().get(1); + assertEquals("e:someExtraWithAttributes", secondExtraInfoElement.getName()); + assertEquals("", secondExtraInfoElement.getContent()); + assertEquals(2, secondExtraInfoElement.getAttributes().size()); + assertEquals("foo", secondExtraInfoElement.getAttributes().get("attr1")); + assertEquals("bar", secondExtraInfoElement.getAttributes().get("attr2")); + assertEquals(0, secondExtraInfoElement.getNestedExtraInfoHolder().size()); + } + + public void testExtraInfosNested() throws Exception { + ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings, + getClass().getResource("test-extrainfo-nested.xml"), true); + assertNotNull(md); + assertEquals(4, md.getExtraInfo().size()); + assertEquals("56576", md.getExtraInfo().get("e:someExtra")); + assertEquals(2, md.getExtraInfos().size()); + ExtraInfoHolder someExtraElement = md.getExtraInfos().get(0); + assertEquals("e:someExtra", someExtraElement.getName()); + assertEquals("56576", someExtraElement.getContent()); + assertEquals(0, someExtraElement.getAttributes().size()); + assertEquals(0, someExtraElement.getNestedExtraInfoHolder().size()); + ExtraInfoHolder someExtraElementWithAttributes = md.getExtraInfos().get(1); + assertEquals("e:someExtraWithAttributes", someExtraElementWithAttributes.getName()); + assertEquals("", someExtraElementWithAttributes.getContent()); + assertEquals(2, someExtraElementWithAttributes.getAttributes().size()); + assertEquals("foo", someExtraElementWithAttributes.getAttributes().get("attr1")); + assertEquals("bar", someExtraElementWithAttributes.getAttributes().get("attr2")); + assertEquals(1, someExtraElementWithAttributes.getNestedExtraInfoHolder().size()); + ExtraInfoHolder anotherExtraInfoElement = someExtraElementWithAttributes.getNestedExtraInfoHolder().get(0); + assertEquals("e:anotherExtraInfo", anotherExtraInfoElement.getName()); + assertEquals("", anotherExtraInfoElement.getContent()); + assertEquals(1, anotherExtraInfoElement.getAttributes().size()); + assertEquals("foobar", anotherExtraInfoElement.getAttributes().get("myattribute")); + assertEquals(1, anotherExtraInfoElement.getNestedExtraInfoHolder().size()); + ExtraInfoHolder yetAnotherExtraInfoElement = anotherExtraInfoElement.getNestedExtraInfoHolder().get(0); + assertEquals("e:yetAnotherExtraInfo", yetAnotherExtraInfoElement.getName()); + assertEquals("", yetAnotherExtraInfoElement.getContent()); + assertEquals(1, yetAnotherExtraInfoElement.getAttributes().size()); + assertEquals("value", yetAnotherExtraInfoElement.getAttributes().get("anAttribute")); + assertEquals(0, yetAnotherExtraInfoElement.getNestedExtraInfoHolder().size()); + } + public void testBug60() throws Exception { ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings, getClass().getResource("test-bug60.xml"), true); diff --git a/test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriterTest.java b/test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriterTest.java index ef922afb..e869313e 100644 --- a/test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriterTest.java +++ b/test/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriterTest.java @@ -110,6 +110,34 @@ public class XmlModuleDescriptorWriterTest extends TestCase { '\r', '\n'); assertEquals(expected, wrote); } + + public void testExtraInfos() throws Exception { + ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor( + new IvySettings(), + XmlModuleDescriptorWriterTest.class.getResource("test-extrainfo.xml"), false); + XmlModuleDescriptorWriter.write(md, LICENSE, dest); + + assertTrue(dest.exists()); + String wrote = FileUtil.readEntirely(new BufferedReader(new FileReader(dest))) + .replaceAll("\r\n", "\n").replace('\r', '\n'); + String expected = readEntirely("test-write-extrainfo.xml").replaceAll("\r\n", "\n") + .replace('\r', '\n'); + assertEquals(expected, wrote); + } + + public void testExtraInfosWithNestedElement() throws Exception { + ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor( + new IvySettings(), + XmlModuleDescriptorWriterTest.class.getResource("test-extrainfo-nested.xml"), false); + XmlModuleDescriptorWriter.write(md, LICENSE, dest); + + assertTrue(dest.exists()); + String wrote = FileUtil.readEntirely(new BufferedReader(new FileReader(dest))) + .replaceAll("\r\n", "\n").replace('\r', '\n'); + String expected = readEntirely("test-write-extrainfo-nested.xml").replaceAll("\r\n", "\n") + .replace('\r', '\n'); + assertEquals(expected, wrote); + } public void testExtends() throws Exception { ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor( diff --git a/test/java/org/apache/ivy/plugins/parser/xml/test-extrainfo-nested.xml b/test/java/org/apache/ivy/plugins/parser/xml/test-extrainfo-nested.xml new file mode 100644 index 00000000..ad0be44e --- /dev/null +++ b/test/java/org/apache/ivy/plugins/parser/xml/test-extrainfo-nested.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + This module is great !
+ You can use it especially with myconf1 and myconf2, and myconf4 is not too bad too. +
+ + 56576 + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/test/java/org/apache/ivy/plugins/parser/xml/test-extrainfo.xml b/test/java/org/apache/ivy/plugins/parser/xml/test-extrainfo.xml new file mode 100644 index 00000000..d767d239 --- /dev/null +++ b/test/java/org/apache/ivy/plugins/parser/xml/test-extrainfo.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + This module is great !
+ You can use it especially with myconf1 and myconf2, and myconf4 is not too bad too. +
+ + 56576 + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/test/java/org/apache/ivy/plugins/parser/xml/test-write-extrainfo-nested.xml b/test/java/org/apache/ivy/plugins/parser/xml/test-write-extrainfo-nested.xml new file mode 100644 index 00000000..590fffc8 --- /dev/null +++ b/test/java/org/apache/ivy/plugins/parser/xml/test-write-extrainfo-nested.xml @@ -0,0 +1,81 @@ + + + + + + + This module is <b>great</b> !<br/> + You can use it especially with myconf1 and myconf2, and myconf4 is not too bad too. + + 56576 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/java/org/apache/ivy/plugins/parser/xml/test-write-extrainfo.xml b/test/java/org/apache/ivy/plugins/parser/xml/test-write-extrainfo.xml new file mode 100644 index 00000000..a0ac1948 --- /dev/null +++ b/test/java/org/apache/ivy/plugins/parser/xml/test-write-extrainfo.xml @@ -0,0 +1,77 @@ + + + + + + + This module is <b>great</b> !<br/> + You can use it especially with myconf1 and myconf2, and myconf4 is not too bad too. + + 56576 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +