From 3196fad33efc3630d4c8012c7eb9b834818ee902 Mon Sep 17 00:00:00 2001
From: fmallet <freddy.mallet@gmail.com>
Date: Wed, 15 Sep 2010 21:41:32 +0000
Subject: SONAR-1766 - The PMD XPath rule can now be duplicated through the
 Sonar user interface and is operational.

---
 plugins/sonar-pmd-plugin/pmd-result.xml            |  2 +-
 .../org/sonar/plugins/pmd/PmdProfileExporter.java  | 64 +++++++++++++++++----
 .../org/sonar/plugins/pmd/PmdProfileImporter.java  | 51 +++++++++++++----
 .../sonar/plugins/pmd/PmdRulePriorityMapper.java   | 66 ----------------------
 .../org/sonar/plugins/pmd/xml/PmdProperty.java     | 20 +++++--
 .../java/org/sonar/plugins/pmd/xml/PmdRule.java    | 23 ++------
 .../java/org/sonar/plugins/pmd/xml/PmdRuleset.java | 14 -----
 .../sonar/plugins/pmd/PmdConfigurationTest.java    |  2 +-
 .../sonar/plugins/pmd/PmdProfileExporterTest.java  |  7 ++-
 .../org/sonar/plugins/pmd/export_simple.xml        |  8 ++-
 .../org/sonar/plugins/pmd/export_xpath_rules.xml   | 12 ++--
 plugins/sonar-pmd-plugin/toto.txt                  | 10 ++++
 12 files changed, 143 insertions(+), 136 deletions(-)
 delete mode 100644 plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdRulePriorityMapper.java
 create mode 100644 plugins/sonar-pmd-plugin/toto.txt

(limited to 'plugins/sonar-pmd-plugin')

diff --git a/plugins/sonar-pmd-plugin/pmd-result.xml b/plugins/sonar-pmd-plugin/pmd-result.xml
index 03947144c7f..79c2c41e634 100644
--- a/plugins/sonar-pmd-plugin/pmd-result.xml
+++ b/plugins/sonar-pmd-plugin/pmd-result.xml
@@ -1,3 +1,3 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<pmd version="4.2.5" timestamp="2010-09-15T16:58:28.966">
+<pmd version="4.2.5" timestamp="2010-09-15T23:37:59.801">
 </pmd>
\ No newline at end of file
diff --git a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileExporter.java b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileExporter.java
index 23f84558299..db8d56ba93d 100644
--- a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileExporter.java
+++ b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileExporter.java
@@ -20,10 +20,16 @@
 package org.sonar.plugins.pmd;
 
 import java.io.IOException;
+import java.io.StringWriter;
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.jdom.CDATA;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.profiles.ProfileExporter;
 import org.sonar.api.profiles.RulesProfile;
@@ -35,8 +41,6 @@ import org.sonar.plugins.pmd.xml.PmdProperty;
 import org.sonar.plugins.pmd.xml.PmdRule;
 import org.sonar.plugins.pmd.xml.PmdRuleset;
 
-import com.thoughtworks.xstream.XStream;
-
 public class PmdProfileExporter extends ProfileExporter {
 
   public PmdProfileExporter() {
@@ -83,18 +87,58 @@ public class PmdProfileExporter extends ProfileExporter {
       rule.setMessage(rule.getProperty(PmdConstants.XPATH_MESSAGE_PARAM).getValue());
       rule.removeProperty(PmdConstants.XPATH_MESSAGE_PARAM);
       PmdProperty xpathExp = rule.getProperty(PmdConstants.XPATH_EXPRESSION_PARAM);
-      xpathExp.setValue("" + xpathExp.getValue() + "");
+      xpathExp.setCdataValue(xpathExp.getValue());
       rule.setClazz(PmdConstants.XPATH_CLASS);
       rule.setName(sonarRuleKey);
     }
   }
 
-  protected String exportPmdRulesetToXml(PmdRuleset tree) {
-    XStream xstream = new XStream();
-    xstream.setClassLoader(getClass().getClassLoader());
-    xstream.processAnnotations(PmdRuleset.class);
-    xstream.processAnnotations(PmdRule.class);
-    xstream.processAnnotations(PmdProperty.class);
-    return xstream.toXML(tree);
+  protected String exportPmdRulesetToXml(PmdRuleset pmdRuleset) {
+    Element eltRuleset = new Element("resultset");
+    for (PmdRule pmdRule : pmdRuleset.getPmdRules()) {
+      Element eltRule = new Element("rule");
+      addAttribute(eltRule, "ref", pmdRule.getRef());
+      addAttribute(eltRule, "class", pmdRule.getClazz());
+      addAttribute(eltRule, "message", pmdRule.getMessage());
+      addAttribute(eltRule, "name", pmdRule.getName());
+      addChild(eltRule, "priority", pmdRule.getPriority());
+      if (pmdRule.hasProperties()) {
+        Element eltProperties = new Element("properties");
+        eltRule.addContent(eltProperties);
+        for (PmdProperty prop : pmdRule.getProperties()) {
+          Element eltProperty = new Element("property");
+          eltProperty.setAttribute("name", prop.getName());
+          if (prop.isCdataValue()) {
+            Element eltValue = new Element("value");
+            eltValue.addContent(new CDATA(prop.getCdataValue()));
+            eltProperty.addContent(eltValue);
+          } else {
+            eltProperty.setAttribute("value", prop.getValue());
+          }
+          eltProperties.addContent(eltProperty);
+        }
+      }
+      eltRuleset.addContent(eltRule);
+    }
+    XMLOutputter serializer = new XMLOutputter(Format.getPrettyFormat());
+    StringWriter xml = new StringWriter();
+    try {
+      serializer.output(new Document(eltRuleset), xml);
+    } catch (IOException e) {
+      throw new SonarException("A exception occured while generating the PMD configuration file.", e);
+    }
+    return xml.toString();
+  }
+
+  private void addChild(Element elt, String name, String text) {
+    if (text != null) {
+      elt.addContent(new Element(name).setText(text));
+    }
+  }
+
+  private void addAttribute(Element elt, String name, String value) {
+    if (value != null) {
+      elt.setAttribute(name, value);
+    }
   }
 }
diff --git a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileImporter.java b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileImporter.java
index 06f7d47efba..21f3968d9ea 100644
--- a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileImporter.java
+++ b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdProfileImporter.java
@@ -20,7 +20,12 @@
 package org.sonar.plugins.pmd;
 
 import java.io.Reader;
+import java.util.List;
 
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.Namespace;
+import org.jdom.input.SAXBuilder;
 import org.sonar.api.profiles.ProfileImporter;
 import org.sonar.api.profiles.RulesProfile;
 import org.sonar.api.resources.Java;
@@ -33,8 +38,6 @@ import org.sonar.plugins.pmd.xml.PmdProperty;
 import org.sonar.plugins.pmd.xml.PmdRule;
 import org.sonar.plugins.pmd.xml.PmdRuleset;
 
-import com.thoughtworks.xstream.XStream;
-
 public class PmdProfileImporter extends ProfileImporter {
 
   private final RuleFinder ruleFinder;
@@ -76,15 +79,43 @@ public class PmdProfileImporter extends ProfileImporter {
 
   protected PmdRuleset parsePmdRuleset(Reader pmdConfigurationFile, ValidationMessages messages) {
     try {
-      XStream xstream = new XStream();
-      xstream.setClassLoader(getClass().getClassLoader());
-      xstream.processAnnotations(PmdRuleset.class);
-      xstream.processAnnotations(PmdRule.class);
-      xstream.processAnnotations(PmdProperty.class);
-      return (PmdRuleset) xstream.fromXML(pmdConfigurationFile);
-    } catch (RuntimeException e) {
-      messages.addErrorText("The PMD configuration file is not valide: " + e.getMessage());
+      SAXBuilder parser = new SAXBuilder();
+      Document dom = parser.build(pmdConfigurationFile);
+      Element eltResultset = dom.getRootElement();
+      Namespace namespace = eltResultset.getNamespace();
+      PmdRuleset pmdResultset = new PmdRuleset();
+      for (Element eltRule : getChildren(eltResultset, "rule", namespace)) {
+        PmdRule pmdRule = new PmdRule(eltRule.getAttributeValue("ref"));
+        parsePmdPriority(eltRule, pmdRule, namespace);
+        parsePmdProperties(eltRule, pmdRule, namespace);
+        pmdResultset.addRule(pmdRule);
+      }
+      return pmdResultset;
+    } catch (Exception e) {
+      messages.addErrorText("The PMD configuration file is not valid : " + e.getMessage());
       return new PmdRuleset();
     }
   }
+
+  private List<Element> getChildren(Element parent, String childName, Namespace namespace) {
+    if (namespace == null) {
+      return (List<Element>) parent.getChildren(childName);
+    } else {
+      return (List<Element>) parent.getChildren(childName, namespace);
+    }
+  }
+
+  private void parsePmdProperties(Element eltRule, PmdRule pmdRule, Namespace namespace) {
+    for (Element eltProperties : getChildren(eltRule, "properties", namespace)) {
+      for (Element eltProperty : getChildren(eltProperties, "property", namespace)) {
+        pmdRule.addProperty(new PmdProperty(eltProperty.getAttributeValue("name"), eltProperty.getAttributeValue("value")));
+      }
+    }
+  }
+
+  private void parsePmdPriority(Element eltRule, PmdRule pmdRule, Namespace namespace) {
+    for (Element eltPriority : getChildren(eltRule, "priority", namespace)) {
+      pmdRule.setPriority(eltPriority.getValue());
+    }
+  }
 }
diff --git a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdRulePriorityMapper.java b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdRulePriorityMapper.java
deleted file mode 100644
index 2b7e73a480a..00000000000
--- a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/PmdRulePriorityMapper.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2009 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.plugins.pmd;
-
-import org.sonar.api.rules.RulePriority;
-import org.sonar.api.rules.RulePriorityMapper;
-
-public class PmdRulePriorityMapper implements RulePriorityMapper<String, String> {
-
-  public RulePriority from(String level) {
-    if ("1".equals(level)) {
-      return RulePriority.BLOCKER;
-    }
-    if ("2".equals(level)) {
-      return RulePriority.CRITICAL;
-    }
-    if ("3".equals(level)) {
-      return RulePriority.MAJOR;
-    }
-    if ("4".equals(level)) {
-      return RulePriority.MINOR;
-    }
-    if ("5".equals(level)) {
-      return RulePriority.INFO;
-    }
-    return null;
-  }
-
-
-  public String to(RulePriority priority) {
-    if (priority.equals(RulePriority.BLOCKER)) {
-      return "1";
-    }
-    if (priority.equals(RulePriority.CRITICAL)) {
-      return "2";
-    }
-    if (priority.equals(RulePriority.MAJOR)) {
-      return "3";
-    }
-    if (priority.equals(RulePriority.MINOR)) {
-      return "4";
-    }
-    if (priority.equals(RulePriority.INFO)) {
-      return "5";
-    }
-    throw new IllegalArgumentException("Level not supported: " + priority);
-  }
-
-}
diff --git a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdProperty.java b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdProperty.java
index 91d1800c803..a7d502979b3 100644
--- a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdProperty.java
+++ b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdProperty.java
@@ -19,17 +19,13 @@
  */
 package org.sonar.plugins.pmd.xml;
 
-import com.thoughtworks.xstream.annotations.XStreamAlias;
-import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
-
-@XStreamAlias("property")
 public class PmdProperty {
 
-  @XStreamAsAttribute
   private String name;
 
-  @XStreamAsAttribute
   private String value;
+  
+  private String cdataValue;
 
   public PmdProperty(String name, String value) {
     this.name = name;
@@ -43,8 +39,20 @@ public class PmdProperty {
   public String getValue() {
     return value;
   }
+  
+  public String getCdataValue() {
+    return cdataValue;
+  }
+  
+  public boolean isCdataValue(){
+    return cdataValue != null;
+  }
 
   public void setValue(String value) {
     this.value = value;
   }
+  
+  public void setCdataValue(String cdataValue) {
+    this.cdataValue = cdataValue;
+  }
 }
diff --git a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRule.java b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRule.java
index 3a15b33b487..87305df110f 100644
--- a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRule.java
+++ b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRule.java
@@ -22,37 +22,18 @@ package org.sonar.plugins.pmd.xml;
 import java.util.ArrayList;
 import java.util.List;
 
-import com.thoughtworks.xstream.annotations.XStreamAlias;
-import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
-import com.thoughtworks.xstream.annotations.XStreamOmitField;
-
-@XStreamAlias("rule")
 public class PmdRule implements Comparable<String> {
 
-  @XStreamAsAttribute
   private String ref;
 
   private String priority;
 
-  @XStreamAsAttribute
   private String name;
 
-  @XStreamAsAttribute
   private String message;
 
   private List<PmdProperty> properties = new ArrayList<PmdProperty>();
 
-  @XStreamOmitField
-  private String description; // NOSONAR unused private field
-
-  @XStreamOmitField
-  private String exclude;// NOSONAR unused private field
-
-  @XStreamOmitField
-  private String example;// NOSONAR unused private field
-
-  @XStreamAsAttribute
-  @XStreamAlias(value = "class")
   private String clazz;// NOSONAR unused private field
 
   public PmdRule(String ref) {
@@ -136,4 +117,8 @@ public class PmdRule implements Comparable<String> {
   public String getName() {
     return name;
   }
+
+  public boolean hasProperties() {
+    return properties != null && !properties.isEmpty();
+  }
 }
\ No newline at end of file
diff --git a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRuleset.java b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRuleset.java
index 52e887164c2..9df698a6a95 100644
--- a/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRuleset.java
+++ b/plugins/sonar-pmd-plugin/src/main/java/org/sonar/plugins/pmd/xml/PmdRuleset.java
@@ -19,29 +19,15 @@
  */
 package org.sonar.plugins.pmd.xml;
 
-import com.thoughtworks.xstream.annotations.XStreamAlias;
-import com.thoughtworks.xstream.annotations.XStreamImplicit;
-import com.thoughtworks.xstream.annotations.XStreamOmitField;
-
 import java.util.ArrayList;
 import java.util.List;
 
-@XStreamAlias("ruleset")
 public class PmdRuleset {
 
   private String description;
 
-  @XStreamImplicit
   private List<PmdRule> rules = new ArrayList<PmdRule>();
 
-  @XStreamOmitField
-  @XStreamAlias(value = "exclude-pattern")
-  private String excludePattern;//NOSONAR unused private field
-
-  @XStreamOmitField
-  @XStreamAlias(value = "include-pattern")
-  private String includePattern;//NOSONAR unused private field
-
   public PmdRuleset() {
   }
 
diff --git a/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdConfigurationTest.java b/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdConfigurationTest.java
index 66891be6562..a54c00f7f78 100644
--- a/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdConfigurationTest.java
+++ b/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdConfigurationTest.java
@@ -45,7 +45,7 @@ public class PmdConfigurationTest {
     assertThat(rulesets.size(), is(1));
     File xmlFile = new File(rulesets.get(0));
     assertThat(xmlFile.exists(), is(true));
-    assertThat(FileUtils.readFileToString(xmlFile), is("<ruleset/>"));
+    assertThat(FileUtils.readFileToString(xmlFile), is("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<resultset />\r\n\r\n"));
   }
 
   @Test
diff --git a/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdProfileExporterTest.java b/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdProfileExporterTest.java
index 7eef7270b4e..594c4cbc75f 100644
--- a/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdProfileExporterTest.java
+++ b/plugins/sonar-pmd-plugin/src/test/java/org/sonar/plugins/pmd/PmdProfileExporterTest.java
@@ -13,6 +13,7 @@ import java.io.StringWriter;
 import java.util.Collection;
 import java.util.List;
 
+import org.apache.commons.lang.StringUtils;
 import org.junit.Test;
 import org.sonar.api.platform.ServerFileSystem;
 import org.sonar.api.profiles.RulesProfile;
@@ -26,6 +27,8 @@ import org.sonar.plugins.pmd.xml.PmdRule;
 import org.sonar.test.TestUtils;
 import org.xml.sax.SAXException;
 
+import com.thoughtworks.xstream.io.xml.JDomReader;
+
 public class PmdProfileExporterTest {
 
   private PmdProfileExporter exporter = new PmdProfileExporter();
@@ -43,7 +46,7 @@ public class PmdProfileExporterTest {
 
     StringWriter xmlOutput = new StringWriter();
     exporter.exportProfile(rulesProfile, xmlOutput);
-    assertEquals(TestUtils.getResourceContent("/org/sonar/plugins/pmd/export_simple.xml"), xmlOutput.toString());
+    assertEquals(TestUtils.getResourceContent("/org/sonar/plugins/pmd/export_simple.xml"), StringUtils.remove(xmlOutput.toString(), '\r'));
   }
 
   @Test
@@ -58,7 +61,7 @@ public class PmdProfileExporterTest {
     xpath.setParameter(PmdConstants.XPATH_EXPRESSION_PARAM, "//FieldDeclaration");
     xpath.setParameter(PmdConstants.XPATH_MESSAGE_PARAM, "This is bad");
     exporter.exportProfile(profile, xmlOutput);
-    assertEquals(TestUtils.getResourceContent("/org/sonar/plugins/pmd/export_xpath_rules.xml"), xmlOutput.toString());
+    assertEquals(TestUtils.getResourceContent("/org/sonar/plugins/pmd/export_xpath_rules.xml"), StringUtils.remove(xmlOutput.toString(), '\r'));
   }
 
   @Test
diff --git a/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_simple.xml b/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_simple.xml
index 0d1527930bb..aeb69c7f6a4 100644
--- a/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_simple.xml
+++ b/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_simple.xml
@@ -1,8 +1,9 @@
-<ruleset>
+<?xml version="1.0" encoding="UTF-8"?>
+<resultset>
   <rule ref="rulesets/coupling.xml/CouplingBetweenObjects">
     <priority>2</priority>
     <properties>
-      <property name="threshold" value="20"/>
+      <property name="threshold" value="20" />
     </properties>
   </rule>
   <rule ref="rulesets/coupling.xml/ExcessiveImports">
@@ -11,4 +12,5 @@
   <rule ref="rulesets/design.xml/UseNotifyAllInsteadOfNotify">
     <priority>4</priority>
   </rule>
-</ruleset>
\ No newline at end of file
+</resultset>
+
diff --git a/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.xml b/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.xml
index 33e7ce9f1cd..1031a6bf008 100644
--- a/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.xml
+++ b/plugins/sonar-pmd-plugin/src/test/resources/org/sonar/plugins/pmd/export_xpath_rules.xml
@@ -1,8 +1,12 @@
-<ruleset>
-  <rule name="MyOwnRule" message="This is bad" class="net.sourceforge.pmd.rules.XPathRule">
+<?xml version="1.0" encoding="UTF-8"?>
+<resultset>
+  <rule class="net.sourceforge.pmd.rules.XPathRule" message="This is bad" name="MyOwnRule">
     <priority>3</priority>
     <properties>
-      <property name="xpath" value="//FieldDeclaration"/>
+      <property name="xpath">
+        <value><![CDATA[//FieldDeclaration]]></value>
+      </property>
     </properties>
   </rule>
-</ruleset>
\ No newline at end of file
+</resultset>
+
diff --git a/plugins/sonar-pmd-plugin/toto.txt b/plugins/sonar-pmd-plugin/toto.txt
new file mode 100644
index 00000000000..08cd17216b9
--- /dev/null
+++ b/plugins/sonar-pmd-plugin/toto.txt
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resultset>
+  <rule class="net.sourceforge.pmd.rules.XPathRule" message="This is bad" name="MyOwnRule">
+    <priority>3</priority>
+    <properties>
+      <property name="xpath" value="//FieldDeclaration" />
+    </properties>
+  </rule>
+</resultset>
+
-- 
cgit v1.2.3