/*
 * 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
 *
 *   https://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.
 */

import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.JavaClass;

/**
 * Patch all Utf8 constants in the given class file <em>file</em>.class and save the result in _<em>file</em>.class.
 *
 * Usage: PatchClass <oldstring> <newstring> files
 */
public class PatchClass {

    public static void main(final String[] argv) throws Exception {
        final String[] fileName = new String[argv.length];
        int files = 0;

        if (argv.length < 3) {
            System.err.println("Usage: patch <oldstring> <newstring> file1.class...");
            System.exit(-1);
        }

        for (int i = 2; i < argv.length; i++) {
            fileName[files++] = argv[i];
        }

        for (int i = 0; i < files; i++) {
            final ClassParser parser = new ClassParser(fileName[i]);
            final JavaClass javaClass = parser.parse();

            patchIt(argv[0], argv[1], javaClass.getConstantPool().getConstantPool());

            // Dump the changed class to a new file
            javaClass.dump("_" + fileName[i]);
            System.out.println("Results saved in: _" + fileName[i]);
        }
    }

    /*
     * Replace all occurrences of string "<em>old</em>" with "<em>replacement</em>" in all Utf8 constants
     */
    private static void patchIt(final String old, final String replacement, final Constant[] constantPool) {
        ConstantUtf8 c;
        String str;
        int index, oldIndex;
        StringBuilder buf;

        // Loop through constant pool
        for (short i = 0; i < constantPool.length; i++) {
            if (constantPool[i] instanceof ConstantUtf8) { // Utf8 string found
                try {
                    c = (ConstantUtf8) constantPool[i]; // Get the string
                    str = c.getBytes();

                    if ((index = str.indexOf(old)) != -1) { // 'old' found in str
                        buf = new StringBuilder(); // target buffer
                        oldIndex = 0; // String start offset

                        // While we have something to replace
                        while ((index = str.indexOf(old, oldIndex)) != -1) {
                            buf.append(str.substring(oldIndex, index)); // append prefix
                            buf.append(replacement); // append 'replacement'

                            oldIndex = index + old.length(); // Skip 'old'.length chars
                        }

                        buf.append(str.substring(oldIndex)); // append rest of string
                        str = buf.toString();

                        // Finally push the new string back to the constant pool
                        c = new ConstantUtf8(str);
                        constantPool[i] = c;
                    }
                } catch (final StringIndexOutOfBoundsException e) { // Should not occur
                    System.err.println(e);
                }
            }
        }
    }
}
