// TITLE     : strsubst() extended. Fixes bugs 4276, 6145 and 9123
// AUTHOR (C): Samuel GOUGEON - Le Mans (France) - sgougeon@free.fr
// RELEASE   : 1.0 (2013-11-09)
// LICENSE   : CeCILL 
//   http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
//   http://www.cecill.info/licences/Licence_CeCILL_V2.1-fr.txt
// SOFTWARE  : for Scilab > 5.5.0  http://www.scilab.org/
// SUPPORTED OS: Windows, Linux, Mac OS
// LIMITATIONS: can't be run in -nwni mode (batch mode)
// DISCLAIMER: No warranty of any kind is provided. Use "as is"
// EXAMPLES  : run strsubst() without input arguments to display examples.

function resu = strsubst(text, pattern, replace, option)

    Sclass = "java.lang.String"
    [lhs, rhs] = argn()

    if rhs>2 then
        if text==[] then
            resu = []
            return
        end
        
        sizeText = size(text)

        // Chosing the Java method: litteral, regexp, regexp once
        // ------------------------------------------------------
        jmeth = 2  // "replace"
        if rhs>3 then
            if option=="r" then
                jmeth = 1  // "replaceAll"
            elseif or(option==["ro" "or"])
                jmeth = 3  // "replaceFirst"
            else
                msg = _("%s: Wrong value for input argument #%d: Must be in the set {%s}.\n")
                error(msprintf(msg, "strsubst", 4, "''r'',''ro''"))
            end
        else
            option = ""
        end
        
        // Compiling the meta-method
        // -------------------------
        try
            arrayRep = jnewInstance("arrayRep")
        catch
            jimport(Sclass)
            arrayRep = jcompile("arrayRep", ..
            ["public class arrayRep {"
             "public static String[] replaceInArray(int meth, String arr[], String[] pat, String[] rep) {"
             "    int nbelem = arr.length; "
             "    String tmp;"
             "    String[] out = new String[nbelem];"
             "    if (meth==1) { "
             "    for (int i = 0; i < nbelem; i++) { "
             "        tmp = arr[i]; "
             "        for (int p = 0; p < pat.length; p++) { "
             "            tmp = tmp.replaceAll(pat[p], rep[p]); "
             "            } "
             "        out[i] = tmp; "
             "        } "
             "    } "
             "    else if (meth==2) {"
             "    for (int i = 0; i < nbelem; i++) { "
             "        tmp = arr[i]; "
             "        for (int p = 0; p < pat.length; p++) { "
             "            tmp = tmp.replace(pat[p], rep[p]); "
             "            } "
             "        out[i] = tmp; "
             "        } "
             "    } "
             "    else {"
             "    for (int i = 0; i < nbelem; i++) { "
             "        tmp = arr[i]; "
             "        for (int p = 0; p < pat.length; p++) { "
             "            tmp = tmp.replaceFirst(pat[p], rep[p]); "
             "            } "
             "        out[i] = tmp; "
             "        } "
             "    } "
             "    return out; "
             "    }"
             "}"]);
         end
        pattern = stripblanks(pattern)

        // Pre-processing the patterns: Java does not accept "/../modifiers" syntax.
        //  - modifiers must be inserted as (?#) switches in head of regexp
        //  - delimiters are not expected
        if option~="" then
            // http://docs.oracle.com/javase/tutorial/essential/regex/pattern.html
            // Extracting and checking delimiters:
            delim = part(pattern, 1)
            forbid = strsplit(("a":"z") + ("A":"Z") + ("0":"9"))'
            nforbid = sum(grep(delim, forbid))
            if nforbid>0 then
                msg = _("%s: Delimiters must be non alphanumeric (%d pattern(s) concerned)")
                error(msprintf(msg, "strsubst", nforbid))
            end
            // Checking the closing delimiter and extracting modifiers:
            modifiers = strrchr(pattern, delim) // should include the closing delimiter
            Nunclosed = length(find((modifiers==pattern)))
            if Nunclosed>0
                msg = _("%s: the delimiter in the pattern is sometimes unclosed (%d pattern(s) concerned)")
                error(msprintf(msg, "strsubst", Nunclosed))
            end
            // Supported modifiers:
            // u => (?u) : unicode case: set by default
            // i => (?i) : case insensitive
            // m => (?m) : multiline
            // x => (?x) : ignores comments starting with # until the end of the line
            // s => (?s) : "." matches also EOL
            Modifiers = emptystr(modifiers)+"(?u"
            for m = ["i" "m" "x" "s"]
                im = grep(modifiers,m)
                Modifiers(im) = Modifiers(im)+m
            end
            Modifiers = Modifiers + ")"
            pattern = strncpy(pattern, length(pattern)-strcspn(strrev(pattern), delim)-1)
            pattern = Modifiers + part(pattern, 2:$)
        end

         // Formating argins
         // ----------------
        text    = text(:)'
        pattern = pattern(:)'
        replace = replace(:)'
        if  size(pattern,"*")==1 then
            replace = replace(1)
        elseif size(replace,"*")==1
            replace = emptystr(pattern) + replace
        elseif size(replace,"*")~=size(pattern,"*")
            msg = _("%s: Incompatible input arguments #%d and #%d: Same sizes expected.\n")
            error(msprintf(msg, "strsubst", 2, 3))
        end
        
        // Calling the routine
        // -------------------
        resu = arrayRep.replaceInArray(jmeth, text, pattern, replace)
        
        // Post-formatting the result
        resu = matrix(resu, sizeText)

    else  // Inline examples with strsubst()
          // -------------------------------
        todo = [_("// Literal substitution. Unicode chars supported:")
                "strsubst(sc, ""l"", ""L"")"
                _("// with matrices of Patterns and respective Replacements:")
                "strsubst(sc, [""l"" ""α""], [""L"" ""A""]) "
                _("// Regular expression (regexp), ""ro""= Replace Once: only the first match is replaced:")
                "strsubst(""abcABCabc"", ""/[^^]a/i"", ""*"", ""ro"")"
                _("// Regexp, all occurrences replaced. /i = case insensitive. /imxs supported: ")
                "strsubst(sc, ""/(l+)/i"", ""é"", ""r"")"
                _("// Regexp, with groups replacements:")
                "strsubst(sc, ""/(.?)(l+)/i"", ""$2$1"", ""r"")"
                ]
        T = "sc = [""Hello"" ""Hólà"" ""ALLO"" ""Γεια σας""];"
        write(%io(2),">> "+T)
        execstr(T)
        for ex = todo'
            if part(ex,1:2)~="//" then
                write(%io(2), ">> "+ex)
                execstr("r="+ex)
                disp(r)
            else
                write(%io(2), ascii(10)+ex)
            end
        end
        resu = ""
    end
endfunction
