001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.ivy.plugins.parser.xml;
019:
020: import java.io.BufferedInputStream;
021: import java.io.BufferedReader;
022: import java.io.File;
023: import java.io.FileOutputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.io.InputStreamReader;
027: import java.io.OutputStream;
028: import java.io.OutputStreamWriter;
029: import java.io.PrintWriter;
030: import java.net.URL;
031: import java.util.ArrayList;
032: import java.util.Arrays;
033: import java.util.Collection;
034: import java.util.Collections;
035: import java.util.Date;
036: import java.util.Iterator;
037: import java.util.List;
038: import java.util.Map;
039: import java.util.Stack;
040: import java.util.StringTokenizer;
041:
042: import javax.xml.parsers.ParserConfigurationException;
043:
044: import org.apache.ivy.Ivy;
045: import org.apache.ivy.core.module.id.ModuleId;
046: import org.apache.ivy.core.module.id.ModuleRevisionId;
047: import org.apache.ivy.core.settings.IvySettings;
048: import org.apache.ivy.plugins.namespace.NameSpaceHelper;
049: import org.apache.ivy.plugins.namespace.Namespace;
050: import org.apache.ivy.plugins.parser.ParserSettings;
051: import org.apache.ivy.plugins.repository.Resource;
052: import org.apache.ivy.plugins.repository.file.FileResource;
053: import org.apache.ivy.plugins.repository.url.URLResource;
054: import org.apache.ivy.util.Message;
055: import org.apache.ivy.util.XMLHelper;
056: import org.apache.ivy.util.extendable.ExtendableItemHelper;
057: import org.xml.sax.Attributes;
058: import org.xml.sax.SAXException;
059: import org.xml.sax.SAXParseException;
060: import org.xml.sax.ext.LexicalHandler;
061: import org.xml.sax.helpers.DefaultHandler;
062:
063: /**
064: * Used to update ivy files. Uses ivy file as source and not ModuleDescriptor to preserve as much as
065: * possible the original syntax
066: */
067: public final class XmlModuleDescriptorUpdater {
068: private static final int MAX_HEADER_LENGTH = 10000;
069: //CheckStyle:StaticVariableName| OFF
070: //LINE_SEPARATOR is actually a constant, but we have to modify it for the tests
071: public static String LINE_SEPARATOR = System
072: .getProperty("line.separator");
073:
074: //CheckStyle:StaticVariableName| ON
075:
076: private XmlModuleDescriptorUpdater() {
077: }
078:
079: /**
080: * used to copy a module descriptor xml file (also known as ivy file) and update the revisions
081: * of its dependencies, its status and revision
082: *
083: * @param srcURL
084: * the url of the source module descriptor file
085: * @param destFile
086: * The file to which the updated module descriptor should be output
087: * @param resolvedRevisions
088: * Map from ModuleId of dependencies to new revision (as String)
089: * @param status
090: * the new status, null to keep the old one
091: * @param revision
092: * the new revision, null to keep the old one
093: */
094: public static void update(URL srcURL, File destFile,
095: final Map resolvedRevisions, final String status,
096: final String revision, final Date pubdate,
097: String[] confsToExclude) throws IOException, SAXException {
098: update(null, srcURL, destFile, resolvedRevisions, status,
099: revision, pubdate, null, false, confsToExclude);
100: }
101:
102: public static void update(final ParserSettings settings,
103: URL srcURL, File destFile, final Map resolvedRevisions,
104: final String status, final String revision,
105: final Date pubdate, final Namespace ns,
106: final boolean replaceInclude, String[] confsToExclude)
107: throws IOException, SAXException {
108: if (destFile.getParentFile() != null) {
109: destFile.getParentFile().mkdirs();
110: }
111: OutputStream destStream = new FileOutputStream(destFile);
112: try {
113: update(settings, srcURL, destStream, resolvedRevisions,
114: status, revision, pubdate, ns, replaceInclude,
115: confsToExclude);
116: } finally {
117: try {
118: destStream.close();
119: } catch (IOException e) {
120: Message.warn("failed to close a stream : "
121: + e.toString());
122: }
123: }
124: }
125:
126: public static void update(final ParserSettings settings,
127: URL srcURL, OutputStream destFile,
128: final Map resolvedRevisions, final String status,
129: final String revision, final Date pubdate,
130: final Namespace ns, final boolean replaceInclude,
131: String[] confsToExclude) throws IOException, SAXException {
132: InputStream in = srcURL.openStream();
133: try {
134: update(settings, srcURL, in, destFile, resolvedRevisions,
135: status, revision, pubdate, ns, replaceInclude,
136: confsToExclude);
137: } finally {
138: try {
139: in.close();
140: } catch (IOException e) {
141: Message.warn("failed to close a stream : "
142: + e.toString());
143: }
144: try {
145: destFile.close();
146: } catch (IOException e) {
147: Message.warn("failed to close a stream : "
148: + e.toString());
149: }
150: }
151:
152: }
153:
154: public static void update(final IvySettings settings,
155: InputStream in, Resource res, File destFile,
156: final Map resolvedRevisions, final String status,
157: final String revision, final Date pubdate,
158: final Namespace ns, final boolean replaceInclude,
159: String[] confsToExclude) throws IOException, SAXException {
160: if (destFile.getParentFile() != null) {
161: destFile.getParentFile().mkdirs();
162: }
163: OutputStream fos = new FileOutputStream(destFile);
164: try {
165: //TODO: use resource as input stream context?
166: URL inputStreamContext = null;
167: if (res instanceof URLResource) {
168: inputStreamContext = ((URLResource) res).getURL();
169: } else if (res instanceof FileResource) {
170: inputStreamContext = ((FileResource) res).getFile()
171: .toURL();
172: }
173: update(settings, inputStreamContext, in, fos,
174: resolvedRevisions, status, revision, pubdate, ns,
175: replaceInclude, confsToExclude);
176: } finally {
177: try {
178: in.close();
179: } catch (IOException e) {
180: Message.warn("failed to close a stream : "
181: + e.toString());
182: }
183: try {
184: fos.close();
185: } catch (IOException e) {
186: Message.warn("failed to close a stream : "
187: + e.toString());
188: }
189: }
190: }
191:
192: private static class UpdaterHandler extends DefaultHandler
193: implements LexicalHandler {
194:
195: private final ParserSettings settings;
196:
197: private final PrintWriter out;
198:
199: private final Map resolvedRevisions;
200:
201: private final String status;
202:
203: private final String revision;
204:
205: private final Date pubdate;
206:
207: private final Namespace ns;
208:
209: private final boolean replaceInclude;
210:
211: private boolean inHeader = true;
212:
213: private final List confs;
214:
215: private final URL relativePathCtx;
216:
217: public UpdaterHandler(final ParserSettings settings,
218: final PrintWriter out, final Map resolvedRevisions,
219: final String status, final String revision,
220: final Date pubdate, final Namespace ns,
221: final boolean replaceInclude, final String[] confs,
222: final URL relativePathCtx) {
223: this .settings = settings;
224: this .out = out;
225: this .resolvedRevisions = resolvedRevisions;
226: this .status = status;
227: this .revision = revision;
228: this .pubdate = pubdate;
229: this .ns = ns;
230: this .replaceInclude = replaceInclude;
231: this .relativePathCtx = relativePathCtx;
232: if (confs != null) {
233: this .confs = Arrays.asList(confs);
234: } else {
235: this .confs = Collections.EMPTY_LIST;
236: }
237: }
238:
239: // never print *ln* cause \n is found in copied characters stream
240: // nor do we need do handle indentation, original one is maintained except for attributes
241:
242: private String organisation = null;
243:
244: private String defaultConfMapping = null; // defaultConfMapping of imported
245:
246: // configurations, if any
247:
248: private Boolean confMappingOverride = null; // confMappingOverride of imported
249:
250: // configurations, if any
251:
252: private String justOpen = null; // used to know if the last open tag was empty, to
253:
254: // adjust termination with /> instead of ></qName>
255:
256: private Stack context = new Stack();
257:
258: private Stack buffers = new Stack();
259:
260: private Stack confAttributeBuffers = new Stack();
261:
262: public void startElement(String uri, String localName,
263: String qName, Attributes attributes)
264: throws SAXException {
265: inHeader = false;
266: if (justOpen != null) {
267: write(">");
268: }
269: context.push(qName);
270: if ("info".equals(qName)) {
271: infoStarted(attributes);
272: } else if (replaceInclude && "include".equals(qName)
273: && context.contains("configurations")) {
274: //TODO, in the case of !replaceInclude, we should still replace the relative path
275: //by an absolute path.
276: includeStarted(attributes);
277: } else if ("ivy-module/dependencies/dependency"
278: .equals(getContext())) {
279: startElementInDependency(attributes);
280: } else if ("dependencies".equals(qName)) {
281: startDependencies(attributes);
282: } else if ("ivy-module/configurations/conf"
283: .equals(getContext())) {
284: startElementInConfigurationsConf(qName, attributes);
285: } else if ("ivy-module/publications/artifact/conf"
286: .equals(getContext())
287: || "ivy-module/dependencies/dependency/conf"
288: .equals(getContext())
289: || "ivy-module/dependencies/dependency/artifact/conf"
290: .equals(getContext())) {
291: buffers.push(new ExtendedBuffer(getContext()));
292: ((ExtendedBuffer) confAttributeBuffers.peek())
293: .setDefaultPrint(false);
294: String confName = substitute(settings, attributes
295: .getValue("name"));
296: if (!confs.contains(confName)) {
297: ((ExtendedBuffer) confAttributeBuffers.peek())
298: .setPrint(true);
299: ((ExtendedBuffer) buffers.peek()).setPrint(true);
300: write("<" + qName);
301: for (int i = 0; i < attributes.getLength(); i++) {
302: write(" "
303: + attributes.getQName(i)
304: + "=\""
305: + substitute(settings, attributes
306: .getValue(i)) + "\"");
307: }
308: }
309: } else if ("ivy-module/publications/artifact"
310: .equals(getContext())
311: || "ivy-module/dependencies/dependency/artifact"
312: .equals(getContext())) {
313: ExtendedBuffer buffer = new ExtendedBuffer(getContext());
314: buffers.push(buffer);
315: confAttributeBuffers.push(buffer);
316: write("<" + qName);
317: buffer
318: .setDefaultPrint(attributes.getValue("conf") == null);
319: for (int i = 0; i < attributes.getLength(); i++) {
320: String attName = attributes.getQName(i);
321: if ("conf".equals(attName)) {
322: String confName = substitute(settings,
323: attributes.getValue("conf"));
324: String newConf = removeConfigurationsFromList(
325: confName, confs);
326: if (newConf.length() > 0) {
327: write(" " + attributes.getQName(i) + "=\""
328: + newConf + "\"");
329: ((ExtendedBuffer) buffers.peek())
330: .setPrint(true);
331: }
332: } else {
333: write(" "
334: + attributes.getQName(i)
335: + "=\""
336: + substitute(settings, attributes
337: .getValue(i)) + "\"");
338: }
339: }
340: } else {
341: // copy
342: write("<" + qName);
343: for (int i = 0; i < attributes.getLength(); i++) {
344: write(" "
345: + attributes.getQName(i)
346: + "=\""
347: + substitute(settings, attributes
348: .getValue(i)) + "\"");
349: }
350: }
351: justOpen = qName;
352: // indent.append("\t");
353: }
354:
355: private void startElementInConfigurationsConf(String qName,
356: Attributes attributes) {
357: buffers.push(new ExtendedBuffer(getContext()));
358: String confName = substitute(settings, attributes
359: .getValue("name"));
360: if (!confs.contains(confName)) {
361: ((ExtendedBuffer) buffers.peek()).setPrint(true);
362: String extend = substitute(settings, attributes
363: .getValue("extends"));
364: if (extend != null) {
365: for (StringTokenizer tok = new StringTokenizer(
366: extend, ", "); tok.hasMoreTokens();) {
367: String current = tok.nextToken();
368: if (confs.contains(current)) {
369: throw new IllegalArgumentException(
370: "Cannot exclude a configuration which is extended.");
371: }
372: }
373: }
374:
375: write("<" + qName);
376: for (int i = 0; i < attributes.getLength(); i++) {
377: write(" "
378: + attributes.getQName(i)
379: + "=\""
380: + substitute(settings, attributes
381: .getValue(i)) + "\"");
382: }
383: }
384: }
385:
386: private void startDependencies(Attributes attributes) {
387: // copy
388: write("<dependencies");
389: for (int i = 0; i < attributes.getLength(); i++) {
390: String attName = attributes.getQName(i);
391: if ("defaultconfmapping".equals(attName)) {
392: String newMapping = removeConfigurationsFromMapping(
393: substitute(settings, attributes
394: .getValue("defaultconfmapping")),
395: confs);
396: if (newMapping.length() > 0) {
397: write(" " + attributes.getQName(i) + "=\""
398: + newMapping + "\"");
399: }
400: } else {
401: write(" "
402: + attributes.getQName(i)
403: + "=\""
404: + substitute(settings, attributes
405: .getValue(i)) + "\"");
406: }
407: }
408: // add default conf mapping if needed
409: if (defaultConfMapping != null
410: && attributes.getValue("defaultconfmapping") == null) {
411: String newMapping = removeConfigurationsFromMapping(
412: defaultConfMapping, confs);
413: if (newMapping.length() > 0) {
414: write(" defaultconfmapping=\"" + newMapping + "\"");
415: }
416: }
417: // add confmappingoverride if needed
418: if (confMappingOverride != null
419: && attributes.getValue("confmappingoverride") == null) {
420: write(" confmappingoverride=\""
421: + confMappingOverride.toString() + "\"");
422: }
423: }
424:
425: private void startElementInDependency(Attributes attributes) {
426: ExtendedBuffer buffer = new ExtendedBuffer(getContext());
427: buffers.push(buffer);
428: confAttributeBuffers.push(buffer);
429: buffer
430: .setDefaultPrint(attributes.getValue("conf") == null
431: || attributes.getValue("conf").trim()
432: .length() == 0);
433: write("<dependency");
434: String org = substitute(settings, attributes
435: .getValue("org"));
436: org = org == null ? organisation : org;
437: String module = substitute(settings, attributes
438: .getValue("name"));
439: String branch = substitute(settings, attributes
440: .getValue("branch"));
441:
442: // look for the branch used in resolved revisions
443: if (branch == null) {
444: ModuleId mid = ModuleId.newInstance(org, module);
445: if (ns != null) {
446: mid = NameSpaceHelper.transform(mid, ns
447: .getToSystemTransformer());
448: }
449: for (Iterator iter = resolvedRevisions.keySet()
450: .iterator(); iter.hasNext();) {
451: ModuleRevisionId mrid = (ModuleRevisionId) iter
452: .next();
453: if (mrid.getModuleId().equals(mid)) {
454: branch = mrid.getBranch();
455: break;
456: }
457: }
458: }
459:
460: String revision = substitute(settings, attributes
461: .getValue("rev"));
462: ModuleRevisionId localMrid = ModuleRevisionId
463: .newInstance(
464: org,
465: module,
466: branch,
467: revision,
468: ExtendableItemHelper
469: .getExtraAttributes(
470: attributes,
471: XmlModuleDescriptorParser.DEPENDENCY_REGULAR_ATTRIBUTES));
472: ModuleRevisionId systemMrid = ns == null ? localMrid : ns
473: .getToSystemTransformer().transform(localMrid);
474:
475: for (int i = 0; i < attributes.getLength(); i++) {
476: String attName = attributes.getQName(i);
477: if ("rev".equals(attName)) {
478: String rev = (String) resolvedRevisions
479: .get(systemMrid);
480: if (rev != null) {
481: write(" rev=\"" + rev + "\"");
482: } else {
483: write(" rev=\"" + systemMrid.getRevision()
484: + "\"");
485: }
486: } else if ("org".equals(attName)) {
487: write(" org=\"" + systemMrid.getOrganisation()
488: + "\"");
489: } else if ("name".equals(attName)) {
490: write(" name=\"" + systemMrid.getName() + "\"");
491: } else if ("branch".equals(attName)) {
492: write(" branch=\"" + systemMrid.getBranch() + "\"");
493: } else if ("conf".equals(attName)) {
494: String oldMapping = substitute(settings, attributes
495: .getValue("conf"));
496: if (oldMapping.length() > 0) {
497: String newMapping = removeConfigurationsFromMapping(
498: oldMapping, confs);
499: if (newMapping.length() > 0) {
500: write(" conf=\"" + newMapping + "\"");
501: ((ExtendedBuffer) buffers.peek())
502: .setPrint(true);
503: }
504: }
505: } else {
506: write(" "
507: + attName
508: + "=\""
509: + substitute(settings, attributes
510: .getValue(attName)) + "\"");
511: }
512: }
513:
514: if (systemMrid.getBranch() != null
515: && attributes.getIndex("branch") == -1) {
516: // this dependency is on a specific branch, we set it explicitly in the updated file
517: write(" branch=\"" + systemMrid.getBranch() + "\"");
518: }
519: }
520:
521: private void includeStarted(Attributes attributes)
522: throws SAXException {
523: final ExtendedBuffer buffer = new ExtendedBuffer(
524: getContext());
525: buffers.push(buffer);
526: try {
527: URL url;
528: if (settings != null) {
529: url = settings.getRelativeUrlResolver().getURL(
530: relativePathCtx,
531: settings.substitute(attributes
532: .getValue("file")),
533: settings.substitute(attributes
534: .getValue("url")));
535: } else {
536: //TODO : settings can be null, but I don't why.
537: //Check if the next code is correct in that case
538: String fileName = attributes.getValue("file");
539: if (fileName == null) {
540: String urlStr = attributes.getValue("url");
541: url = new URL(urlStr);
542: } else {
543: url = new File(fileName).toURL();
544: }
545: }
546: XMLHelper.parse(url, null, new DefaultHandler() {
547: private boolean insideConfigurations = false;
548:
549: private boolean doIndent = false;
550:
551: public void startElement(String uri,
552: String localName, String qName,
553: Attributes attributes) throws SAXException {
554: if ("configurations".equals(qName)) {
555: insideConfigurations = true;
556: String defaultconf = substitute(
557: settings,
558: attributes
559: .getValue("defaultconfmapping"));
560: if (defaultconf != null) {
561: defaultConfMapping = defaultconf;
562: }
563: String mappingOverride = substitute(
564: settings,
565: attributes
566: .getValue("confmappingoverride"));
567: if (mappingOverride != null) {
568: confMappingOverride = Boolean
569: .valueOf(mappingOverride);
570: }
571: } else if ("conf".equals(qName)
572: && insideConfigurations) {
573: String confName = substitute(settings,
574: attributes.getValue("name"));
575: if (!confs.contains(confName)) {
576: buffer.setPrint(true);
577: if (doIndent) {
578: write("/>\n\t\t");
579: }
580: String extend = substitute(settings,
581: attributes.getValue("extends"));
582: if (extend != null) {
583: for (StringTokenizer tok = new StringTokenizer(
584: extend, ", "); tok
585: .hasMoreTokens();) {
586: String current = tok
587: .nextToken();
588: if (confs.contains(current)) {
589: throw new IllegalArgumentException(
590: "Cannot exclude a "
591: + "configuration which is extended.");
592: }
593: }
594:
595: }
596:
597: write("<" + qName);
598: for (int i = 0; i < attributes
599: .getLength(); i++) {
600: write(" "
601: + attributes.getQName(i)
602: + "=\""
603: + substitute(
604: settings,
605: attributes
606: .getValue(i))
607: + "\"");
608: }
609: doIndent = true;
610: }
611: }
612: }
613:
614: public void endElement(String uri,
615: String localName, String name)
616: throws SAXException {
617: if ("configurations".equals(name)) {
618: insideConfigurations = false;
619: }
620: }
621: });
622: } catch (Exception e) {
623: Message
624: .warn("exception occured while importing configurations: "
625: + e.getMessage());
626: throw new SAXException(e);
627: }
628: }
629:
630: private void infoStarted(Attributes attributes) {
631: organisation = substitute(settings, attributes
632: .getValue("organisation"));
633: String module = substitute(settings, attributes
634: .getValue("module"));
635: String rev = revision;
636: if (rev == null) {
637: rev = substitute(settings, attributes
638: .getValue("revision"));
639: }
640: ModuleRevisionId localMid = ModuleRevisionId.newInstance(
641: organisation, module, null, rev,
642: ExtendableItemHelper.getExtraAttributes(attributes,
643: new String[] { "organisation", "module",
644: "revision", "status",
645: "publication", "namespace" }));
646: ModuleRevisionId systemMid = ns == null ? localMid : ns
647: .getToSystemTransformer().transform(localMid);
648:
649: write("<info organisation=\""
650: + XMLHelper.escape(systemMid.getOrganisation())
651: + "\" module=\""
652: + XMLHelper.escape(systemMid.getName()) + "\"");
653: if (systemMid.getRevision() != null) {
654: write(" revision=\""
655: + XMLHelper.escape(systemMid.getRevision())
656: + "\"");
657: }
658: if (status != null) {
659: write(" status=\"" + XMLHelper.escape(status) + "\"");
660: } else {
661: write(" status=\""
662: + substitute(settings, attributes
663: .getValue("status")) + "\"");
664: }
665: if (pubdate != null) {
666: write(" publication=\""
667: + Ivy.DATE_FORMAT.format(pubdate) + "\"");
668: } else if (attributes.getValue("publication") != null) {
669: write(" publication=\""
670: + substitute(settings, attributes
671: .getValue("publication")) + "\"");
672: }
673: Collection stdAtts = Arrays.asList(new String[] {
674: "organisation", "module", "revision", "status",
675: "publication", "namespace" });
676: if (attributes.getValue("namespace") != null) {
677: write(" namespace=\""
678: + substitute(settings, attributes
679: .getValue("namespace")) + "\"");
680: }
681: for (int i = 0; i < attributes.getLength(); i++) {
682: if (!stdAtts.contains(attributes.getQName(i))) {
683: write(" "
684: + attributes.getQName(i)
685: + "=\""
686: + substitute(settings, attributes
687: .getValue(i)) + "\"");
688: }
689: }
690: }
691:
692: private void write(String content) {
693: if (buffers.isEmpty()) {
694: out.print(content);
695: } else {
696: ExtendedBuffer buffer = (ExtendedBuffer) buffers.peek();
697: buffer.getBuffer().append(content);
698: }
699: }
700:
701: private String getContext() {
702: StringBuffer buf = new StringBuffer();
703: for (Iterator iter = context.iterator(); iter.hasNext();) {
704: String ctx = (String) iter.next();
705: buf.append(ctx).append("/");
706: }
707: if (buf.length() > 0) {
708: buf.setLength(buf.length() - 1);
709: }
710: return buf.toString();
711: }
712:
713: private String substitute(ParserSettings ivy, String value) {
714: String result = ivy == null ? value : ivy.substitute(value);
715: return XMLHelper.escape(result);
716: }
717:
718: private String removeConfigurationsFromMapping(String mapping,
719: List confsToRemove) {
720: StringBuffer newMapping = new StringBuffer();
721: String mappingSep = "";
722: for (StringTokenizer tokenizer = new StringTokenizer(
723: mapping, ";"); tokenizer.hasMoreTokens();) {
724: String current = tokenizer.nextToken();
725: String[] ops = current.split("->");
726: String[] lhs = ops[0].split(",");
727: List confsToWrite = new ArrayList();
728: for (int j = 0; j < lhs.length; j++) {
729: if (!confs.contains(lhs[j].trim())) {
730: confsToWrite.add(lhs[j]);
731: }
732: }
733: if (!confsToWrite.isEmpty()) {
734: newMapping.append(mappingSep);
735:
736: String sep = "";
737: for (Iterator it = confsToWrite.iterator(); it
738: .hasNext();) {
739: newMapping.append(sep);
740: newMapping.append(it.next());
741: sep = ",";
742: }
743: if (ops.length == 2) {
744: newMapping.append("->");
745: newMapping.append(ops[1]);
746: }
747: mappingSep = ";";
748: }
749: }
750:
751: return newMapping.toString();
752: }
753:
754: private String removeConfigurationsFromList(String list,
755: List confsToRemove) {
756: StringBuffer newList = new StringBuffer();
757: String listSep = "";
758: for (StringTokenizer tokenizer = new StringTokenizer(list,
759: ","); tokenizer.hasMoreTokens();) {
760: String current = tokenizer.nextToken();
761: if (!confsToRemove.contains(current.trim())) {
762: newList.append(listSep);
763: newList.append(current);
764: listSep = ",";
765: }
766: }
767:
768: return newList.toString();
769: }
770:
771: public void characters(char[] ch, int start, int length)
772: throws SAXException {
773: if (justOpen != null) {
774: write(">");
775: justOpen = null;
776: }
777: write(String.valueOf(ch, start, length));
778: }
779:
780: public void endElement(String uri, String localName,
781: String qName) throws SAXException {
782: if (qName.equals(justOpen)) {
783: write("/>");
784: } else {
785: write("</" + qName + ">");
786: }
787:
788: if (!buffers.isEmpty()) {
789: ExtendedBuffer buffer = (ExtendedBuffer) buffers.peek();
790: if (buffer.getContext().equals(getContext())) {
791: buffers.pop();
792: if (buffer.isPrint()) {
793: write(buffer.getBuffer().toString());
794: }
795: }
796: }
797:
798: if (!confAttributeBuffers.isEmpty()) {
799: ExtendedBuffer buffer = (ExtendedBuffer) confAttributeBuffers
800: .peek();
801: if (buffer.getContext().equals(getContext())) {
802: confAttributeBuffers.pop();
803: }
804: }
805:
806: justOpen = null;
807: context.pop();
808: }
809:
810: public void endDocument() throws SAXException {
811: out.print(LINE_SEPARATOR);
812: out.flush();
813: out.close();
814: }
815:
816: public void warning(SAXParseException e) throws SAXException {
817: throw e;
818: }
819:
820: public void error(SAXParseException e) throws SAXException {
821: throw e;
822: }
823:
824: public void fatalError(SAXParseException e) throws SAXException {
825: throw e;
826: }
827:
828: public void endCDATA() throws SAXException {
829: }
830:
831: public void endDTD() throws SAXException {
832: }
833:
834: public void startCDATA() throws SAXException {
835: }
836:
837: public void comment(char[] ch, int start, int length)
838: throws SAXException {
839: if (!inHeader) {
840: StringBuffer comment = new StringBuffer();
841: comment.append(ch, start, length);
842: write("<!--");
843: write(comment.toString());
844: write("-->");
845: }
846: }
847:
848: public void endEntity(String name) throws SAXException {
849: }
850:
851: public void startEntity(String name) throws SAXException {
852: }
853:
854: public void startDTD(String name, String publicId,
855: String systemId) throws SAXException {
856: }
857:
858: }
859:
860: public static void update(final ParserSettings settings,
861: URL inStreamCtx, InputStream inStream,
862: OutputStream outStream, final Map resolvedRevisions,
863: final String status, final String revision,
864: final Date pubdate, final Namespace ns,
865: final boolean replaceInclude, String[] confsToExclude)
866: throws IOException, SAXException {
867: final PrintWriter out = new PrintWriter(new OutputStreamWriter(
868: outStream, "UTF-8"));
869: final BufferedInputStream in = new BufferedInputStream(inStream);
870:
871: in.mark(MAX_HEADER_LENGTH); // assume the header is never larger than 10000 bytes.
872: copyHeader(in, out);
873: in.reset(); // reposition the stream at the beginning
874:
875: try {
876: UpdaterHandler updaterHandler = new UpdaterHandler(
877: settings, out, resolvedRevisions, status, revision,
878: pubdate, ns, replaceInclude, confsToExclude,
879: inStreamCtx);
880: XMLHelper.parse(in, null, updaterHandler, updaterHandler);
881: } catch (ParserConfigurationException e) {
882: IllegalStateException ise = new IllegalStateException(
883: "impossible to update Ivy files: parser problem");
884: ise.initCause(e);
885: throw ise;
886: }
887: }
888:
889: /**
890: * Copy xml header from src url ivy file to given printwriter In fact, copies everything before
891: * <ivy-module to out, except if <ivy-module is not found, in which case nothing is copied. The
892: * prolog <?xml version="..." encoding="...."?> is also replaced by <?xml version="1.0"
893: * encoding="UTF-8"?> if it was present.
894: *
895: * @param in
896: * @param out
897: * @throws IOException
898: */
899: private static void copyHeader(InputStream in, PrintWriter out)
900: throws IOException {
901: BufferedReader r = new BufferedReader(new InputStreamReader(in));
902: String line = r.readLine();
903: if (line != null && line.startsWith("<?xml ")) {
904: out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
905: line = line.substring(line.indexOf(">") + 1, line.length());
906: }
907: for (; line != null; line = r.readLine()) {
908: int index = line.indexOf("<ivy-module");
909: if (index == -1) {
910: out.write(line);
911: out.write(LINE_SEPARATOR);
912: } else {
913: out.write(line.substring(0, index));
914: break;
915: }
916: }
917: // r.close();
918: }
919:
920: private static class ExtendedBuffer {
921: private String context = null;
922:
923: private Boolean print = null;
924:
925: private boolean defaultPrint = false;
926:
927: private StringBuffer buffer = new StringBuffer();
928:
929: ExtendedBuffer(String context) {
930: this .context = context;
931: }
932:
933: boolean isPrint() {
934: if (print == null) {
935: return defaultPrint;
936: }
937: return print.booleanValue();
938: }
939:
940: void setPrint(boolean print) {
941: this .print = Boolean.valueOf(print);
942: }
943:
944: void setDefaultPrint(boolean print) {
945: this .defaultPrint = print;
946: }
947:
948: StringBuffer getBuffer() {
949: return buffer;
950: }
951:
952: String getContext() {
953: return context;
954: }
955: }
956: }
|