This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new dfdd5b1274a CAMEL-19087: camel trace - Added pretty option
dfdd5b1274a is described below

commit dfdd5b1274a5170da5e78c3679ea7e7d52bb31e8
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Sun Mar 5 21:06:44 2023 +0100

    CAMEL-19087: camel trace - Added pretty option
---
 .../apache/camel/util/xml/XmlPrettyPrinter.java    | 182 +++++++++++++++++++++
 .../camel/util/xml/XmlPrettyPrinterTest.java       |  53 ++++++
 .../core/commands/action/CamelTraceAction.java     |  23 ++-
 .../camel/dsl/jbang/core/common/XmlHelper.java     |  34 ++++
 4 files changed, 291 insertions(+), 1 deletion(-)

diff --git 
a/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/XmlPrettyPrinter.java
 
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/XmlPrettyPrinter.java
new file mode 100644
index 00000000000..23a91a2081b
--- /dev/null
+++ 
b/core/camel-xml-jaxp/src/main/java/org/apache/camel/util/xml/XmlPrettyPrinter.java
@@ -0,0 +1,182 @@
+/*
+ * 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.camel.util.xml;
+
+import java.io.ByteArrayInputStream;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import static org.apache.camel.util.StringHelper.padString;
+
+public final class XmlPrettyPrinter {
+
+    @FunctionalInterface
+    public interface ColorPrintElement {
+
+        int DECLARATION = 1;
+        int ELEMENT = 2;
+        int ATTRIBUTE_KEY = 3;
+        int ATTRIBUTE_VALUE = 4;
+        int ATTRIBUTE_EQUAL = 5;
+        int ATTRIBUTE_QUOTE = 6;
+        int VALUE = 7;
+
+        String color(int type, String value);
+    }
+
+    public static String colorPrint(String xml, int blanks, boolean 
declaration, ColorPrintElement color)
+            throws Exception {
+        return doParse(xml, blanks, declaration, color);
+    }
+
+    /**
+     * Pretty print the XML (does not use DOM or any kind of parser)
+     *
+     * @param  xml    the XML
+     * @param  blanks number of blanks to use as indent
+     * @return        the XML in pretty, without XML declaration
+     */
+    public static String pettyPrint(String xml, int blanks) throws Exception {
+        return doParse(xml, blanks, false, new NoopColor());
+    }
+
+    /**
+     * Pretty print the XML (does not use DOM or any kind of parser)
+     *
+     * @param  xml         the XML
+     * @param  blanks      number of blanks to use as indent
+     * @param  declaration whether to include XML declaration
+     * @return             the XML in pretty
+     */
+    public static String pettyPrint(String xml, int blanks, boolean 
declaration) throws Exception {
+        return doParse(xml, blanks, declaration, new NoopColor());
+    }
+
+    private static class NoopColor implements ColorPrintElement {
+
+        @Override
+        public String color(int type, String value) {
+            return value;
+        }
+    }
+
+    private static String doParse(String xml, int blanks, boolean declaration, 
ColorPrintElement color) throws Exception {
+        SAXParser parser;
+        final SAXParserFactory factory = SAXParserFactory.newInstance();
+        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl";, 
true);
+        factory.setFeature("http://xml.org/sax/features/namespaces";, false);
+        factory.setFeature("http://xml.org/sax/features/validation";, false);
+        
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar";,
 false);
+        
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd";,
 false);
+        
factory.setFeature("http://xml.org/sax/features/external-parameter-entities";, 
false);
+        
factory.setFeature("http://xml.org/sax/features/external-general-entities";, 
false);
+        parser = factory.newSAXParser();
+
+        final StringBuilder sb = new StringBuilder();
+        final DefaultHandler handler = new DefaultHandler() {
+            int indent;
+
+            @Override
+            public void declaration(String version, String encoding, String 
standalone) throws SAXException {
+                if (declaration) {
+                    StringBuilder lb = new StringBuilder();
+                    lb.append("<?xml");
+                    if (version != null) {
+                        lb.append(" version=\"").append(version).append("\"");
+                    }
+                    if (encoding != null) {
+                        lb.append(" 
encoding=\"").append(encoding).append("\"");
+                    }
+                    if (standalone != null) {
+                        lb.append(" 
standalone=\"").append(encoding).append("\"");
+                    }
+                    lb.append("?>");
+
+                    String value = color.color(ColorPrintElement.DECLARATION, 
lb.toString());
+                    sb.append(value);
+                    sb.append("\n");
+                }
+            }
+
+            @Override
+            public void startElement(String uri, String localName, String 
qName, Attributes attributes) throws SAXException {
+                sb.append(padString(indent, blanks));
+
+                StringBuilder lb = new StringBuilder();
+                lb.append("<");
+                lb.append(qName);
+                String value = color.color(ColorPrintElement.ELEMENT, 
lb.toString());
+                sb.append(value);
+
+                lb.setLength(0);
+                for (int i = 0; i < attributes.getLength(); i++) {
+                    String k = color.color(ColorPrintElement.ATTRIBUTE_KEY, 
attributes.getQName(i));
+                    String v = color.color(ColorPrintElement.ATTRIBUTE_VALUE, 
attributes.getValue(i));
+                    String eq = color.color(ColorPrintElement.ATTRIBUTE_EQUAL, 
"=");
+                    String quote = 
color.color(ColorPrintElement.ATTRIBUTE_QUOTE, "\"");
+                    lb.append(" 
").append(k).append(eq).append(quote).append(v).append(quote);
+                }
+                sb.append(lb);
+
+                value = color.color(ColorPrintElement.ELEMENT, ">");
+                sb.append(value);
+                sb.append("\n");
+
+                indent++;
+            }
+
+            @Override
+            public void endElement(String uri, String localName, String qName) 
throws SAXException {
+                --indent;
+
+                StringBuilder lb = new StringBuilder();
+                lb.append("</");
+                lb.append(qName);
+                lb.append(">");
+
+                sb.append(padString(indent, blanks));
+                String value = color.color(ColorPrintElement.ELEMENT, 
lb.toString());
+                sb.append(value);
+                if (indent > 0) {
+                    sb.append("\n");
+                }
+            }
+
+            @Override
+            public void characters(char[] ch, int start, int length) throws 
SAXException {
+                char[] chars = new char[length];
+                System.arraycopy(ch, start, chars, 0, length);
+                String value = color.color(ColorPrintElement.VALUE, new 
String(chars));
+
+                sb.append(padString(indent, blanks));
+                sb.append(value);
+                sb.append("\n");
+            }
+        };
+
+        parser.parse(new ByteArrayInputStream(xml.getBytes()), handler);
+        return sb.toString();
+    }
+
+}
diff --git 
a/core/camel-xml-jaxp/src/test/java/org/apache/camel/util/xml/XmlPrettyPrinterTest.java
 
b/core/camel-xml-jaxp/src/test/java/org/apache/camel/util/xml/XmlPrettyPrinterTest.java
new file mode 100644
index 00000000000..e84dd9589d2
--- /dev/null
+++ 
b/core/camel-xml-jaxp/src/test/java/org/apache/camel/util/xml/XmlPrettyPrinterTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.camel.util.xml;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class XmlPrettyPrinterTest {
+
+    @Test
+    public void testPrettyPrint() throws Exception {
+        String xml = "<?xml version=\"1.0\" 
encoding=\"UTF-8\"?><root><tag><nested>hello</nested></tag></root>";
+        String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                          + "<root>\n"
+                          + "  <tag>\n"
+                          + "    <nested>\n"
+                          + "      hello\n"
+                          + "    </nested>\n"
+                          + "  </tag>\n"
+                          + "</root>";
+        String pretty = XmlPrettyPrinter.pettyPrint(xml, 2, true);
+        Assertions.assertEquals(expected, pretty);
+    }
+
+    @Test
+    public void testPrettyPrintNoDecl() throws Exception {
+        String xml = "<?xml version=\"1.0\" 
encoding=\"UTF-8\"?><root><tag><nested>hello</nested></tag></root>";
+        String expected = "<root>\n"
+                          + "  <tag>\n"
+                          + "    <nested>\n"
+                          + "      hello\n"
+                          + "    </nested>\n"
+                          + "  </tag>\n"
+                          + "</root>";
+        String pretty = XmlPrettyPrinter.pettyPrint(xml, 2, false);
+        Assertions.assertEquals(expected, pretty);
+    }
+
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
index df35b0f42de..a178822f3ad 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
@@ -43,6 +43,7 @@ import org.apache.camel.catalog.impl.TimePatternConverter;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.common.JSonHelper;
 import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
+import org.apache.camel.dsl.jbang.core.common.XmlHelper;
 import org.apache.camel.util.StopWatch;
 import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.TimeUtils;
@@ -131,7 +132,7 @@ public class CamelTraceAction extends ActionBaseCommand {
     boolean latest;
 
     @CommandLine.Option(names = { "--pretty" },
-                        description = "Pretty print message body when using 
JSon format")
+                        description = "Pretty print message body when using 
JSon or XML format")
     boolean pretty;
 
     String findAnsi;
@@ -902,6 +903,7 @@ public class CamelTraceAction extends ActionBaseCommand {
             if (value == null) {
                 return "null";
             }
+            boolean json = false;
             String s = value.toString();
             if (!s.isEmpty()) {
                 try {
@@ -911,12 +913,31 @@ public class CamelTraceAction extends ActionBaseCommand {
                     } else {
                         s = JSonHelper.prettyPrint(s, 2);
                     }
+                    if (s != null && !s.isEmpty()) {
+                        json = true;
+                    }
                 } catch (Throwable e) {
                     // ignore as not json
                 }
                 if (s == null || s.isEmpty()) {
                     s = value.toString();
                 }
+                if (!json) {
+                    // try with xml
+                    try {
+                        s = Jsoner.unescape(s);
+                        if (loggingColor) {
+                            s = XmlHelper.colorPrint(s, 2, true);
+                        } else {
+                            s = XmlHelper.prettyPrint(s, 2);
+                        }
+                    } catch (Throwable e) {
+                        // ignore as not xml
+                    }
+                }
+                if (s == null || s.isEmpty()) {
+                    s = value.toString();
+                }
             }
             if (s == null) {
                 return "null";
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/XmlHelper.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/XmlHelper.java
index 452307af4cd..374ad0a3930 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/XmlHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/XmlHelper.java
@@ -21,12 +21,46 @@ import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.xml.XmlPrettyPrinter;
+import org.fusesource.jansi.Ansi;
 
 public final class XmlHelper {
 
     private XmlHelper() {
     }
 
+    /**
+     * Prints the XML in pretty mode (no color).
+     */
+    public static String prettyPrint(String xml, int spaces) throws Exception {
+        return XmlPrettyPrinter.pettyPrint(xml, spaces);
+    }
+
+    /**
+     * Prints the XML with ANSi color (similar to jq)
+     */
+    public static String colorPrint(String xml, int spaces, boolean pretty) 
throws Exception {
+        return XmlPrettyPrinter.colorPrint(xml, spaces, pretty, new 
XmlPrettyPrinter.ColorPrintElement() {
+            @Override
+            public String color(int type, String value) {
+                String s = value != null ? value : "null";
+                if (type == XmlPrettyPrinter.ColorPrintElement.DECLARATION) {
+                    s = 
Ansi.ansi().fgBrightDefault().a(Ansi.Attribute.INTENSITY_FAINT).a(s).reset().toString();
+                } else if (type == XmlPrettyPrinter.ColorPrintElement.ELEMENT) 
{
+                    s = 
Ansi.ansi().fgBright(Ansi.Color.BLUE).a(s).reset().toString();
+                } else if (type == XmlPrettyPrinter.ColorPrintElement.VALUE) {
+                    s = Ansi.ansi().fgDefault().a(s).reset().toString();
+                } else if (type == 
XmlPrettyPrinter.ColorPrintElement.ATTRIBUTE_KEY) {
+                    s = 
Ansi.ansi().fg(Ansi.Color.MAGENTA).a(s).reset().toString();
+                } else if (type == 
XmlPrettyPrinter.ColorPrintElement.ATTRIBUTE_VALUE
+                        || type == 
XmlPrettyPrinter.ColorPrintElement.ATTRIBUTE_QUOTE) {
+                    s = 
Ansi.ansi().fg(Ansi.Color.GREEN).a(s).reset().toString();
+                }
+                return s;
+            }
+        });
+    }
+
     public static DocumentBuilderFactory createDocumentBuilderFactory() {
         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
         factory.setNamespaceAware(true);

Reply via email to