All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.simpleflatmapper.lightningcsv.impl.AsmCharConsumerFactory Maven / Gradle / Ivy

Go to download

Java library to map flat record - ResultSet, csv - to java object with minimum configuration and low footprint.

There is a newer version: 9.0.2
Show newest version
package org.simpleflatmapper.lightningcsv.impl;

import org.simpleflatmapper.lightningcsv.parser.AbstractCharConsumer;
import org.simpleflatmapper.lightningcsv.parser.CellPreProcessor;
import org.simpleflatmapper.lightningcsv.parser.CharBuffer;
import org.simpleflatmapper.lightningcsv.parser.CharConsumerFactory;
import org.simpleflatmapper.lightningcsv.parser.ConfigurableCharConsumer;
import org.simpleflatmapper.lightningcsv.parser.TextFormat;
import org.simpleflatmapper.lightningcsv.parser.UnescapeCellPreProcessor;
import org.simpleflatmapper.ow2asm.ClassReader;
import org.simpleflatmapper.ow2asm.ClassVisitor;
import org.simpleflatmapper.ow2asm.ClassWriter;
import org.simpleflatmapper.ow2asm.FieldVisitor;
import org.simpleflatmapper.ow2asm.MethodVisitor;
import org.simpleflatmapper.ow2asm.Opcodes;
import org.simpleflatmapper.util.FactoryClassLoader;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import static org.simpleflatmapper.lightningcsv.impl.AsmUtils.toTargetTypeDeclaration;
import static org.simpleflatmapper.ow2asm.Opcodes.BIPUSH;
import static org.simpleflatmapper.ow2asm.Opcodes.ICONST_0;
import static org.simpleflatmapper.ow2asm.Opcodes.ICONST_1;
import static org.simpleflatmapper.ow2asm.Opcodes.IRETURN;

public class AsmCharConsumerFactory extends CharConsumerFactory {
    private static final AtomicInteger id = new AtomicInteger();
    
    public AbstractCharConsumer newCharConsumer(TextFormat textFormat, CharBuffer charBuffer, CellPreProcessor cellTransformer, boolean specialisedCharConsumer) {
        if (specialisedCharConsumer) {
            SpecialisationKey key = new SpecialisationKey(cellTransformer.ignoreLeadingSpace(), textFormat, cellTransformer.getClass());

            Constructor constructor;

            if (key.equals(RFC4180)) {
                constructor = RFC4180_CC;
            } else {
                constructor = specialisedCharConsumers.get(key);
                if (constructor == null && specialisedCharConsumers.size() < 64) {
                    constructor = createNewSpecialisedCharConsumer(key);
                }
            }

            if (constructor != null) {
                try {
                    return constructor.newInstance(charBuffer, key.textFormat, cellTransformer);
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage(), e);
                }
            }
        }

        return new ConfigurableCharConsumer(charBuffer, textFormat, cellTransformer);

    }


    private static Constructor createNewSpecialisedCharConsumer(SpecialisationKey key) {
        synchronized (lock) {
            Constructor constructor;
            constructor = specialisedCharConsumers.get(key);
            if (constructor == null) {
                if (specialisedCharConsumers.size() < 64) { // artificial limit to avoid DOS
                    constructor = generateSpecialisedCharConsumer(key);
                    if (constructor != null) {
                        specialisedCharConsumers.put(key, constructor);
                    }
                }
            }
            return constructor;
        }
    }

    @SuppressWarnings("unchecked")
    private static Constructor generateSpecialisedCharConsumer(final SpecialisationKey key) {
        try {
            final String newName = "org/simpleflatmapper/lightningcsv/parser/Asm_"
                    + (key.ignoreLeadingSpace ? "Ils_" : "")
                    + (key.textFormat.yamlComment ? "Yaml_" : "")
                    + "S_" + Integer.toHexString(key.textFormat.separatorChar)
                    + "_Q_" + Integer.toHexString(key.textFormat.quoteChar)
                    + "_E_" + Integer.toHexString(key.textFormat.escapeChar)
                    + key.cellTransformer.getClass().getName().replace('.', '_')
                    + "_CharConsumer_" + id.incrementAndGet();


            final Set oldNames = new HashSet(Arrays.asList("org/simpleflatmapper/lightningcsv/parser/ConfigurableCharConsumer"));
            ClassReader reader = new ClassReader(
                    ConfigurableCharConsumer.class.getResourceAsStream("ConfigurableCharConsumer.class")
            );
            ClassWriter writer = new ClassWriter(reader,      ClassWriter.COMPUTE_MAXS |
                    ClassWriter.COMPUTE_FRAMES);
            ClassVisitor visitor = new ClassVisitor(AsmUtils.API, writer) {
                @Override
                public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
                    super.visit(version, access, newName, signature, superName, interfaces);
                }

                @Override
                public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
                    if (name.equals("cellPreProcessor")) {
                        super.visitField(access, name, toTargetTypeDeclaration(AsmUtils.toAsmType(key.cellTransformer)), toTargetTypeDeclaration(AsmUtils.toGenericAsmType(key.cellTransformer)), value);
                    }
                    return super.visitField(access, name, descriptor, signature, value);
                }

                @Override
                public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
                    MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
                    if (name.equals("ignoreLeadingSpace")) {
                        mv.visitCode();
                        mv.visitInsn(key.ignoreLeadingSpace ? ICONST_1 : ICONST_0);
                        mv.visitInsn(IRETURN);
                        mv.visitMaxs(1, 1);
                        mv.visitEnd();

                        return null;
                    } else if (name.equals("yamlComment")) {
                        mv.visitCode();
                        mv.visitInsn(key.textFormat.yamlComment ? ICONST_1 : ICONST_0);
                        mv.visitInsn(IRETURN);
                        mv.visitMaxs(1, 1);
                        mv.visitEnd();
                        return null;

                    } else if (name.equals("escapeChar")) {
                        mv.visitCode();
                        mv.visitIntInsn(BIPUSH, key.textFormat.escapeChar);
                        mv.visitInsn(IRETURN);
                        mv.visitMaxs(1, 1);
                        mv.visitEnd();
                        return null;

                    } else if (name.equals("separatorChar")) {
                        mv.visitCode();
                        mv.visitIntInsn(BIPUSH, key.textFormat.separatorChar);
                        mv.visitInsn(IRETURN);
                        mv.visitMaxs(1, 1);
                        mv.visitEnd();
                        return null;

                    } else if (name.equals("quoteChar")) {
                        mv.visitCode();
                        mv.visitIntInsn(BIPUSH, key.textFormat.quoteChar);
                        mv.visitInsn(IRETURN);
                        mv.visitMaxs(1, 1);
                        mv.visitEnd();
                        return null;

                    } else {
                        return new MethodVisitor(AsmUtils.API, mv) {
                            
                            
                            public void visitTypeInsn(int i, String s) {
                                if (oldNames.contains(s)) {
                                    s = newName;
                                }
                                mv.visitTypeInsn(i, s);
                            }

                            public void visitFieldInsn(int opcode, String owner, String name, String desc) {
                                if ("cellPreProcessor".equals(name)) {
                                    if (opcode == Opcodes.PUTFIELD) {
                                        mv.visitTypeInsn(Opcodes.CHECKCAST, AsmUtils.toAsmType(key.cellTransformer));
                                        mv.visitFieldInsn(opcode, newName, name, toTargetTypeDeclaration(AsmUtils.toAsmType(key.cellTransformer)));
                                    } else {
                                        mv.visitFieldInsn(opcode, newName, name, toTargetTypeDeclaration(AsmUtils.toAsmType(key.cellTransformer)));
                                    }
                                } else if (oldNames.contains(owner)) {
                                    mv.visitFieldInsn(opcode, newName, name, fix(desc));
                                } else {
                                    mv.visitFieldInsn(opcode, owner, name, fix(desc));
                                }
                            }

                            public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
                                if (owner.equals(AsmUtils.toAsmType(CellPreProcessor.class))) {
                                    mv.visitMethodInsn(opcode, AsmUtils.toAsmType(key.cellTransformer), name, desc, itf);
                                } else if (oldNames.contains(owner)) {
                                    mv.visitMethodInsn(opcode, newName, name, fix(desc), itf);
                                } else {
                                    mv.visitMethodInsn(opcode, owner, name, fix(desc), itf);
                                }
                            }

                            private String fix(String s) {
                                if (s != null) {
                                    Iterator it = oldNames.iterator();
                                    String name;
                                    while (it.hasNext()) {
                                        name = (String) it.next();
                                        if (s.indexOf(name) != -1) {
                                            s = s.replaceAll(name, newName);
                                        }
                                    }
                                }
                                return s;
                            }
                        };
                    }
                }
            };
            reader.accept(visitor, 0);

            byte[] bytes = writer.toByteArray();

            String className = newName.replace('/', '.');
            AsmUtils.writeClassToFile(className, bytes);

            Class clazz = classLoader.registerClass(className, bytes, null);

            return (Constructor) clazz.getConstructor(CharBuffer.class, TextFormat.class, CellPreProcessor.class);

        } catch (Exception e) {
            // ignore
            System.err.println("Error creating specialised parser " + e.getMessage());
        }
        return null;
    }

    private static final FactoryClassLoader classLoader = new FactoryClassLoader(AbstractCharConsumer.class.getClassLoader());
    private static final Object lock = new Object();
    private static final SpecialisationKey RFC4180 = new SpecialisationKey(false, new TextFormat(',', '"', '"', false), UnescapeCellPreProcessor.class);
    private static final Constructor RFC4180_CC;

    static {
        RFC4180_CC = generateSpecialisedCharConsumer(RFC4180);
    }

    private static final ConcurrentHashMap> specialisedCharConsumers =
            new ConcurrentHashMap>();

    private static class SpecialisationKey {
        final boolean ignoreLeadingSpace;
        final TextFormat textFormat;
        final Class cellTransformer;
        private SpecialisationKey(boolean ignoreLeadingSpace, TextFormat textFormat, Class cellTransformer) {
            this.ignoreLeadingSpace = ignoreLeadingSpace;
            this.textFormat = textFormat;
            this.cellTransformer = cellTransformer;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            SpecialisationKey that = (SpecialisationKey) o;

            if (ignoreLeadingSpace != that.ignoreLeadingSpace) return false;
            if (!textFormat.equals(that.textFormat)) return false;
            return cellTransformer.equals(that.cellTransformer);
        }

        @Override
        public int hashCode() {
            int result = (ignoreLeadingSpace ? 1 : 0);
            result = 31 * result + textFormat.hashCode();
            result = 31 * result + cellTransformer.hashCode();
            return result;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy