0

Out product is integrated with Drools and user can add/edit/deletd Drools functions from/to a package by loading the entire package in a simple long scrolling JTextArea and literally directly editing some DRools functions; or, better, copy and paste the whole package after editing it with another instrument (like Notepad++). When the new package is persisted, I have to parse it with its previous version stored in a DB and audit differences in existing functions, or inserted/deleted ones. I already had a class which splits a Drools functions into its component, to which I added this method to recover a snapshot of the function to be stored:

public String getFunctionCode() {
    StringBuilder strBld = new StringBuilder("function ");

    if (this.returnType == null)
        strBld.append("void");
    else
        strBld.append(this.returnType);

    strBld.append(" ");
    strBld.append(this.name);
    strBld.append("(");

    if (this.paramNames != null && !this.paramNames.isEmpty()) {
        int parametersSize = this.paramTypes.size();
        for (int i = 0; i < parametersSize; i++) {
            strBld.append(this.paramTypes.get(i).getName());
            strBld.append(" ");
            strBld.append(this.paramNames.get(i).getName());
            if (i < parametersSize - 1)
                strBld.append(",");
        }
    }

    strBld.append(") {");

    if (this.text == null || this.text.trim().isEmpty())
        strBld.append(" ");
    else
        strBld.append(this.text);

    strBld.append("}\r\n");

    return strBld.toString();
}

where paramNames and paramTypes are simply a List<String> containing the signature of the function, while text contains the body of the function between the two main { } after the signature. It happens only sometimes that all the other functions of package are detected as edited, even if user added or deleted another one. Why this? It this because of some not printable character? If I compare the two snapshots stored on DB with a text diff tool, nothing is detected. To get changed functions, in a for loop I do:

for (FunctionInfo functionInfo : updatedFunctions) {
    FunctionInfo oldFunction = oldPackageInfo.getFunction(functionInfo.getName());

    String oldFunctionBody = oldFunction.getFunctionCode();
    String newFunctionBody = functionInfo.getFunctionCode();

    if (!newFunctionBody.equals(oldFunctionBody)) {
      /* persist an audit record */
    }
}

where updatedFunctions is a List<FunctionInfo> got by retrieving all functions in common between old persisted package and the one arriving from front end, after sorting them by name. Which could be the cause?

UPDATE I started Wildfly in debug mode and I on Eclipse a breakpoint inside the if statement in the previous loop. Than, using this online tool for text diff I compared oldFunctionBody and newFunctionBody of a function I did not edited from the frontend window. It turns out that old untouched functions coming from front end were added with some unprintable characters, overall with many \r. Why this happens?

Old function:

function void disableTestOnDevice(SampleBuilder sample,String devices,String[] devTestCode) {\r\n\t\t   if(devices == null)\r\n\t\t\t   return;\r\n\t       if(sample.getListTransc() != null && sample.getListTransc().size() > 0){\t\r\n\t\t\t\tfor(int i = sample.getListTransc().size()-1; i >= 0; i--){\r\n\t\t\t\t\tViewWorklistBuilder j = (ViewWorklistBuilder)sample.getListTransc().get(i);\r\n\t\t\t\t\tif(j.getDeviceId() == null || j.getDeviceTestCode() == null || j.getDeviceSpecimenCode() == null){\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif(j.getDeviceId().equals(devices)){\r\n\t\t\t\t\t\tif(devTestCode != null){\r\n\t\t\t\t\t\t\tfor(int k = 0;  k < devTestCode.length; k++){\r\n\t\t\t\t\t\t\t\tif(j.getDeviceTestCode().equals(devTestCode[k])){\r\n\t\t\t\t\t\t\t\t\tj.setEnabled(0);\r\n\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\t\t\r\n\t\t\t\t}\r\n\t       }\r\n\t\r\n

New function:

function void disableTestOnDevice(SampleBuilder sample,String devices,String[] devTestCode) {\n\t\t   if(devices == null)\n\t\t\t   return;\n\t       if(sample.getListTransc() != null && sample.getListTransc().size() > 0){\t\n\t\t\t\tfor(int i = sample.getListTransc().size()-1; i >= 0; i--){\n\t\t\t\t\tViewWorklistBuilder j = (ViewWorklistBuilder)sample.getListTransc().get(i);\n\t\t\t\t\tif(j.getDeviceId() == null || j.getDeviceTestCode() == null || j.getDeviceSpecimenCode() == null){\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif(j.getDeviceId().equals(devices)){\n\t\t\t\t\t\tif(devTestCode != null){\n\t\t\t\t\t\t\tfor(int k = 0;  k < devTestCode.length; k++){\n\t\t\t\t\t\t\t\tif(j.getDeviceTestCode().equals(devTestCode[k])){\n\t\t\t\t\t\t\t\t\tj.setEnabled(0);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t}\n\t       }\n\t\r\n
SagittariusA
  • 5,289
  • 15
  • 73
  • 127
  • Can you try to debug an instance when your code detects an incorrect(false) change, which two functions does it actually compare? – Emre Dalkiran Apr 16 '18 at 16:43
  • I’va tried it, and the functions are exactly the same. Same name and same signature. Moreover, if I open OLD_SNAPSHOT and NEW_FUNCTIONS fields in DB table, they contain the same String for that function name – SagittariusA Apr 16 '18 at 16:46
  • I don't know if it is possible but if this situation happens sometimes as you mentioned in the question, you may try to find out what type of change to a function creates false change detection on others. It can give a hint to further solve your problem. – Emre Dalkiran Apr 16 '18 at 16:52
  • I am afraid the reason is about unprintable characters even if I don’t know why it happens if I don’t touch the other functions... – SagittariusA Apr 16 '18 at 16:56
  • 1
    Something like [this](https://stackoverflow.com/a/6199346/8555304) answer before comparing strings may help. – Emre Dalkiran Apr 16 '18 at 17:13
  • 1
    @EmreDalkiran : thank you for your advice, I was just starting thinking about removing all unprintable characters before comparing function body... I hope it helps. I’ll let you know tomorrow in the morning – SagittariusA Apr 16 '18 at 17:19
  • @EmreDalkiran if you want, have a look to my updated question. – SagittariusA Apr 17 '18 at 06:57
  • How do you add and store a function for the first time? – Emre Dalkiran Apr 17 '18 at 08:04
  • By getting all text edited by user from JTextArea with getText() and passing it to Drools parsing core which, somehow, splits that text, into objects, one for each function – SagittariusA Apr 17 '18 at 08:10
  • I am not familiar with Drools but during that process, it may be adding this non-printable chars to provide somehow visually formatted functions. Adding tabs etc. – Emre Dalkiran Apr 17 '18 at 08:12

2 Answers2

1

You need to rewrite the default .equals() method to specify how you think it's equal in your classes where you compare objects, if the default comparison is not working for you.

Edit: See the documentation for Java 7 and the tutorial for javase for more information. Also see this post on when to use StringBuilder.

Raymo111
  • 514
  • 8
  • 24
  • I shouldn’t need to do that because what I’m comparing are common strings so there’s no need to override .equals() method... – SagittariusA Apr 16 '18 at 16:51
  • Have you seen the documentation? StringBuilder may not necessarily return common strings. – Raymo111 Apr 16 '18 at 20:35
  • No...I didn’t know. What do you mean exactly? I had only read that by default String and StringBuilder are unicode-16... – SagittariusA Apr 16 '18 at 21:19
  • That doesn't matter. StringBuilder Strings are different from regular Strings. You need to rewrite a .equals method to compare them. – Raymo111 Apr 16 '18 at 21:36
  • Ok, I will try to have a look on documentation. They always say ti use StringBuilder because it’s very fast and you don’t waste memory... no one ever mentioned about any difference with classic String. And moreover I wouldn’t know how to rewrite and .equals() method... – SagittariusA Apr 16 '18 at 21:39
  • IMO Plain old string would be faster than StringBuilder, so I try not to use StringBuilder unless I have to. Using the `concatenate` symbol `+` with 2 Strings will very well replace the `.append()` method with StringBuilder. – Raymo111 Apr 16 '18 at 21:41
  • That was only what SonarQube said... I switched to StringBuilder as it said I was adding technical debt and slow down performance by doing simple concatenation... if you have that doc, would you please link it? – SagittariusA Apr 16 '18 at 21:43
  • Sorry, I'm not familiar with SonarQube. – Raymo111 Apr 16 '18 at 21:44
  • That’s not a problem, I’m just a (silly) junior developer and I want to learn. If you have that documentation and want to share it, I would be thankful! – SagittariusA Apr 16 '18 at 21:45
  • Thank you for editing your answer but I am afraid I did not find anything which lets me understand that string returned by StringBuilder are not standard strings to be compared with .equals() method...what did you mean exactly? – SagittariusA Apr 17 '18 at 06:40
1

According to your last update. The main difference between the "old function" and the "new function" results are the carry return characters (I think that there is a tab character missing there, too). Anyway, to avoid this kind of issues I recommend you use System.lineSeparator(), introduced in JDK7:

Returns the system-dependent line separator string. It always returns the same value - the initial value of the system property line.separator. On UNIX systems, it returns "\n"; on Microsoft Windows systems it returns "\r\n". Reference: https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#lineSeparator()

Additionally, I recommend you use fixing space size instead of \t due to different application replace it in different ways. I mean in different tab size. So, for example a clean solution could be create a constant called TAB : " " and replace it everywhere you use \t.

final String TAB = "  ";

Finally, I notice that you are returning the value of the StringBuilder as string, then, I guess that when you are doing the comparison you have the text stored in some String variable. Which could be useful if you are initializing as UTF-8 encoding (just for say one).

  • Thank you Cristian for your kind answer. I know that there's a`\r` missing. The problem is that when old function is recover from DB table, somehow a `\r` is added before each `\n\t` group. I really don't know why this happens, because when functions arrives from frontend we only have `\n\t` but after it is stored and recovered (maybe for the first time) we have that further `\r` which makes the difference... why is it added? It seems, for the moment, the only solution before comparing is to do `if (!newFunctionBody.replaceAll("\\p{C}", "").equals(oldFunctionBody.replaceAll("\\p{C}", "")))` – SagittariusA Apr 17 '18 at 14:51
  • 1
    I see, I forgot the first part of you question, sorry. What was the DB that you were using and what is the one that you are using now (Name, version, SO). Are they running in different SO ? . I guess that could be a problem there because carry return change between unix and window. I don´t have any other idea. – Cristian Caram Peñalver Apr 17 '18 at 15:15
  • I use Oracle DB 12 on LINUX machine (if I remember well they are Linux Red Hat distributions). Then, I am developing and running a Swing client of our product in Windows 10 – SagittariusA Apr 17 '18 at 15:36
  • But the problem occurs only the first time when functions are loaded...If I add/delete/edit one after the first time, the others are ignored as expected...that damn thing with added \r character seems to happen only once... – SagittariusA Apr 17 '18 at 15:37
  • 1
    I guess that it is something related with SO due to one (UNIX) uses \n and other (Win) \r\n . But, I don´t have idea what it happens only once, maybe because when functions are loaded they came from db?. Anyway this is just my assumption... Sorry I cannot help you more. – Cristian Caram Peñalver Apr 17 '18 at 19:34