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

com.javanut.pronghorn.pipe.util.build.FROMValidation Maven / Gradle / Ivy

Go to download

Ring buffer based queuing utility for applications that require high performance and/or a small footprint. Well suited for embedded and stream based processing.

There is a newer version: 1.1.27
Show newest version
package com.javanut.pronghorn.pipe.util.build;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;

import javax.xml.parsers.ParserConfigurationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import com.javanut.pronghorn.pipe.DataInputBlobReader;
import com.javanut.pronghorn.pipe.FieldReferenceOffsetManager;
import com.javanut.pronghorn.pipe.MessageSchema;
import com.javanut.pronghorn.pipe.PipeReader;
import com.javanut.pronghorn.pipe.PipeWriter;
import com.javanut.pronghorn.pipe.RawDataSchema;
import com.javanut.pronghorn.pipe.schema.loader.TemplateHandler;
import com.javanut.pronghorn.pipe.token.TokenBuilder;
import com.javanut.pronghorn.pipe.token.TypeMask;
import com.javanut.pronghorn.util.Appendables;

public class FROMValidation {

	public final static Logger logger = LoggerFactory.getLogger(FROMValidation.class);
	
	public static boolean forceCodeGen = false;
	
    private static > boolean testForMatchingFROMs(String templateFile, S schema) {
    	boolean result = false;
    	StringBuilder target = new StringBuilder();
        try {
            FieldReferenceOffsetManager encodedFrom = null;
            try {
                encodedFrom = MessageSchema.from(schema); //TODO: new projects get null pointer here, fix so they are given correct source.
            } catch (NullPointerException npe) {
                //continue with no previous FROM
            }
            result = testForMatchingFROMs(templateFile, encodedFrom, target);           
            if (!result) {
            	System.out.println(target);
            }            
        } catch (Exception e) {
            e.printStackTrace();
            result = false;
        }

        return result;
    }


	private static boolean testForMatchingFROMs(String templateFile,
			FieldReferenceOffsetManager encodedFrom, Appendable target)
			throws ParserConfigurationException, SAXException, IOException {

		boolean result = true;
		FieldReferenceOffsetManager expectedFrom = TemplateHandler.loadFrom(templateFile);
		if (null==expectedFrom) {
		    logger.error("Unable to find: "+templateFile);
		    result = false;
		} else if (null==encodedFrom || !expectedFrom.equals(encodedFrom)) {
		    logger.error("Encoded source:"+expectedFrom);
		    if (null!=encodedFrom) {
		        logger.error("Template file:"+encodedFrom);
		    }
		    logger.error("//replacement source");
		    String nameOfFROM = templateFile.substring(1+templateFile.lastIndexOf('/') );
		    
		    FieldReferenceOffsetManager.buildFROMConstructionSource(target, expectedFrom, "FROM", nameOfFROM);  
		    result = false;
		}
		return result;
	}
    
	public static > boolean checkSchema(String templateFile, Class clazz, boolean forceCode) {
		try {
			FROMValidation.forceCodeGen = forceCode;		
			return checkSchema(templateFile, clazz);
		} finally {
			FROMValidation.forceCodeGen = false;
		}
	}
	
	public static > boolean checkSchema(String templateFile, Class clazz) {
		
		StringBuilder target = new StringBuilder();
	
		S schemaInstance = MessageSchema.findInstance(clazz);		
		
		/////////////////////////
		/////////////////////////
		FieldReferenceOffsetManager expectedFrom = null;
		try {
			expectedFrom = TemplateHandler.loadFrom(templateFile);
		} catch (Exception e2) {
			e2.printStackTrace();
		}
		if (null==expectedFrom) {
			logger.error("Unable to find: {}",templateFile);
			return false;
		}
		/////////////////////////
		/////////////////////////
		
		boolean result = true;
		
		if (null!=schemaInstance) {
						
			try {
			    FieldReferenceOffsetManager encodedFrom = null;
			
		        encodedFrom = MessageSchema.from(schemaInstance);
				
				if (null==encodedFrom || !expectedFrom.equals(encodedFrom)) {
				    logger.error("Encoded source:"+expectedFrom);
				    if (null!=encodedFrom) {
				        logger.error("Template file:"+encodedFrom);
				    }
				    logger.error("//replacement source, encoded from missing or not matching");
				    result = false;
				}
				    				    
			    FieldReferenceOffsetManager.buildFROMConstructionSource(
			    		target, expectedFrom, 
			    		"FROM", templateFile.substring(1+templateFile.lastIndexOf('/') ));  
          
				buildConstructor(target, clazz);//want FROM to be first at the top
			
				//////////////////
			    //////////////////
			    if (!result) {
			    	forceCodeGen = true;
			    }
			    result &= testForMatchingLocators(clazz, expectedFrom, target);
			    //////////////////
			    //////////////////
			    
			} catch (Exception e1) {
			    e1.printStackTrace();
			    result = false;
			}
			
		} else {
			result = false;
			try {
			    logger.error("Encoded source: {}",expectedFrom);
				logger.error("//replacement source, schema instance missing");
				
				FieldReferenceOffsetManager.buildFROMConstructionSource(
						target, 
						expectedFrom, 
						"FROM", 
						templateFile.substring(1+templateFile.lastIndexOf('/') ));
				
				buildConstructor(target, clazz);//want FROM to be first at the top
				
			    //////////////////
			    //////////////////
			    forceCodeGen = true;
			    testForMatchingLocators(clazz, expectedFrom, target);
			    //////////////////
			    //////////////////
								
				
			} catch (Exception e1) {
				logger.error("unable to build FROM {} {}",e1.getClass().getSimpleName(),e1.getMessage());
		
			}
		}

		
		if (!result) {
			System.out.println(target);
		}

		return result;
	}



	private static > void buildConstructor(Appendable target, Class clazz) {
		//show the new constructor
		try {
			
			target.append("\n");
			
			target.append("public "+clazz.getSimpleName()+"() { \n");
			target.append("    super(FROM);\n");
			target.append("}\n");			
			target.append("\n");
			
			target.append("protected "+clazz.getSimpleName()+"("+FieldReferenceOffsetManager.class.getSimpleName()+" from) { \n");
			target.append("    super(from);\n");
			target.append("}\n");			
			target.append("\n");
			
			//show the line needed for adding the instance
			target.append("public static final "+clazz.getSimpleName()+" instance = new "+clazz.getSimpleName()+"();\n");
			target.append("\n");
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private static  boolean testForMatchingLocators(
			Class schemaClass,
			FieldReferenceOffsetManager encodedFrom,
			Appendable target) throws IOException {
		
		
		Field[] fields = schemaClass.getFields();
	    
	    if (MessageSchema.class != schemaClass.getSuperclass()) {
	        System.out.println("all Schema objects must directly extend "+MessageSchema.class.getCanonicalName());
	        return false;
	    }
	    
	    int[] msgStart = encodedFrom.messageStarts;
	    
	    //TODO: at some point we want to code generate low level examples...
	    
	    StringBuilder generatedConstants = new StringBuilder();
	    StringBuilder generatedSwitch = new StringBuilder();
	    StringBuilder generatedConsumers = new StringBuilder();
	    
	    StringBuilder generatedProducersTemp1 = new StringBuilder();
	    StringBuilder generatedProducersTemp2 = new StringBuilder();
	    
	    StringBuilder generatedProducers = new StringBuilder();
		
		boolean success1 = generateSchemaBehavior(encodedFrom, fields, msgStart, generatedConstants, generatedSwitch,
				generatedConsumers, generatedProducersTemp1, generatedProducersTemp2, generatedProducers,
				schemaClass.getSimpleName(), encodedFrom.hasSimpleMessagesOnly);
		
		if (encodedFrom.hasSimpleMessagesOnly) {
			success1 = checkForExampleCode(schemaClass, success1, "consume"); //must find at least 1 consume method
			success1 = checkForExampleCode(schemaClass, success1, "publish"); //must find at least 1 publish method
		}
	    	    
	    boolean success = success1;
    		    
	    if (!success || forceCodeGen) {
	    	//to target, do not log.
	    	target.append(generatedConstants);
	    	target.append("\n");
	    	target.append(generatedSwitch);
	    	target.append("\n");
	    	target.append(generatedConsumers);
	    	target.append("\n");
	    	target.append(generatedProducers);
	    }
	    
	    return success;
	}


	private static boolean generateSchemaBehavior(FieldReferenceOffsetManager encodedFrom, Field[] fields,
			int[] msgStart, StringBuilder generatedConstants, StringBuilder generatedSwitch,
			StringBuilder generatedConsumers, StringBuilder generatedProducersTemp1,
			StringBuilder generatedProducersTemp2, StringBuilder generatedProducers, final String schemaClassName,
			boolean generateExampleMethods) {
		
		if (generateExampleMethods) {
			generatedSwitch.append("public static void consume(Pipe<").append(schemaClassName).append("> input) {\n");
	    	generatedSwitch.append("    while (PipeReader.tryReadFragment(input)) {\n");
	    	generatedSwitch.append("        int msgIdx = PipeReader.getMsgIdx(input);\n");
	    	generatedSwitch.append("        switch(msgIdx) {\n");	    	
	    }
	    
	    boolean success = true;
	    for(int i = 0 ; i=0 && !found) {
    	            String schemaFieldName = fields[j].getName();
    	            if (schemaFieldName.equals(messageConstantName)) {
    	                found = true;    	                
    	                try {
                            int assignedValue = fields[j].getInt(null);
                            if (expectedMsgIdx != assignedValue) {
                                success = false;
                                //logger.error(("//wrong expected value: "+messageConstantName);
                            }                            
    	                } catch (IllegalArgumentException e) {                           
                            e.printStackTrace();
                            found = false;
                        } catch (IllegalAccessException e) {                            
                            e.printStackTrace();
                            found = false;
                        }
    	            }
    	        }
    	        if (!found) {
    	            success = false;
    	            logger.error("//unable to find: {}",messageConstantName);
    	        }
    	        
    	        
    	        int fieldLimit;
    	        if (i+1>=msgStart.length) {
    	            fieldLimit = encodedFrom.fieldIdScript.length;
    	        } else {
    	            fieldLimit = msgStart[i+1];
    	        }
    	            
    	        
    	        for(int fieldIdx = msgStart[i]+1; fieldIdx=0 && !found) {
    	                    String schemaFieldName = fields[j].getName();
    	                    if (schemaFieldName.equals(messageFieldConstantName)) {
    	                        found = true;                       
    	                        try {
    	                            int assignedValue = fields[j].getInt(null);
    	                            if (fieldLOC != assignedValue) {
    	                                success = false;
    	                                //logger.error(("//wrong expected value: "+messageFieldConstantName);
    	                            }                            
    	                        } catch (IllegalArgumentException e) {                           
    	                            e.printStackTrace();
    	                            found = false;
    	                        } catch (IllegalAccessException e) {                            
    	                            e.printStackTrace();
    	                            found = false;
    	                        }
    	                    }
    	                }
    	                if (!found) {
    	                    success = false;
    	                    logger.error("//unable to find: {}",messageFieldConstantName);
    	                }    	                
    	               
    	            }
    	        }
    	        if (generateExampleMethods) {
    	        	appendProduceMethodEnd(generatedProducersTemp1, generatedProducersTemp2, generatedProducers, methodName, messageConstantName, schemaClassName);
    	        	appendConsumeMethodEnd(generatedConsumers);
    	        }
	        }
	    }
	    
	    if (generateExampleMethods) {
	    	generatedSwitch.append("            case -1:\n");
	    	generatedSwitch.append("               //requestShutdown();\n");
	    	generatedSwitch.append("            break;\n");	    	
	    	generatedSwitch.append("        }\n");
	    	generatedSwitch.append("        PipeReader.releaseReadLock(input);\n    }\n}\n");

	    }
		return success;
	}



	private static > boolean checkForExampleCode(Class schemaClass, boolean success, String startsWith) {
		boolean found = false;
    	for(Method m :schemaClass.getMethods()) {
    		if (m.getName().startsWith(startsWith)) {
    			found = true;
    		}
    	}
    	if (!found) {
    		success = false;
    	}
		return success;
	}

	private static > void appendConsumeMethodField(StringBuilder generatedConsumers, 
			                                                                  String varName, String constName, 
			                                                                  int token, String schemaClassName) {
		
		int type = TokenBuilder.extractType(token);
		
		if (TypeMask.isInt(type)) {
			
			generatedConsumers.append("    int ").append(varName).append(" = PipeReader.readInt(input,").append(constName).append(");\n");
				
		} else if (TypeMask.isLong(type)) {
			
			generatedConsumers.append("    long ").append(varName).append(" = PipeReader.readLong(input,").append(constName).append(");\n");

		} else if (TypeMask.isDecimal(type)) {
			
			generatedConsumers.append("    int ").append(varName).append("e = PipeReader.readInt(input,").append(constName).append(");\n");
			generatedConsumers.append("    long ").append(varName).append("m = PipeReader.readLong(input,").append(constName).append(");\n");

		} else if (TypeMask.isText(type)) {    	    	        		    	    	       		
			
			generatedConsumers.append("    StringBuilder ").append(varName).append(" = PipeReader.readUTF8(input,").append(constName)
													   .append(",new StringBuilder(PipeReader.readBytesLength(input,")
													   .append(constName).append(")));\n");
						
		} else if (TypeMask.isByteVector(type)) {
			generatedConsumers
								.append("    DataInputBlobReader<").append(schemaClassName).append("> ")
								.append(varName)
								.append(" = PipeReader.inputStream(input, ")
								.append(constName)
								.append(");\n");
																	     		
		} else {
			throw new UnsupportedOperationException("unknown value "+type);
		}
	}
	
	private static void appendProduceMethodField(StringBuilder argsTemp, StringBuilder bodyTemp, String varName, String constName, int token) {
		
		int type = TokenBuilder.extractType(token);
		
		if (TypeMask.isInt(type)) {
			
			argsTemp.append("int ").append(varName).append(", ");
			bodyTemp.append("        PipeWriter.writeInt(output,").append(constName).append(", ").append(varName).append(");\n");

		} else if (TypeMask.isLong(type)) {
			
			argsTemp.append("long ").append(varName).append(", ");
			bodyTemp.append("        PipeWriter.writeLong(output,").append(constName).append(", ").append(varName).append(");\n");

		} else if (TypeMask.isDecimal(type)) {
			
			argsTemp.append("int ").append(varName).append(", ");
			argsTemp.append("long ").append(varName).append(", ");
			bodyTemp.append("        PipeWriter.writeDecimal(output,").append(constName).append(", ").append(varName).append("e, ").append(varName).append("m);\n");

		} else if (TypeMask.isText(type)) {    	    	        		    	    	       		
			
			argsTemp.append("CharSequence ").append(varName).append(", ");
			bodyTemp.append("        PipeWriter.writeUTF8(output,").append(constName).append(", ").append(varName).append(");\n");
		
		} else if (TypeMask.isByteVector(type)) {
			
			argsTemp.append("byte[] ").append(varName).append("Backing, ");
			argsTemp.append("int ").append(varName).append("Position, ");
			argsTemp.append("int ").append(varName).append("Length, ");
			bodyTemp.append("        PipeWriter.writeBytes(output,").append(constName).append(", ")
			              .append(varName).append("Backing, ").append(varName).append("Position, ").append(varName).append("Length")
			              .append(");\n");
			
		} else {
			throw new UnsupportedOperationException("unknown value "+type);
		}
	}

	
    private static void appendConsumeMethodBegin(StringBuilder generatedConsumers, String methodName, String schemaClassName ) {
    	generatedConsumers.append("public static void consume").append(methodName).append("(Pipe<").append(schemaClassName).append("> input) {\n");
	}

    
    private static void appendConsumeMethodEnd(StringBuilder generatedConsumers) {
    	generatedConsumers.append("}\n");
	}
    
    private static void appendProduceMethodEnd(StringBuilder argsTemp, StringBuilder bodyTemp, StringBuilder generatedProducers, 
    		                                    String methodName, String messageConst, String schemaClassName) {

    	if (argsTemp.length()>0) {//remove last comma and space if found
    		argsTemp.setLength(argsTemp.length()-2);
    	}
		
		generatedProducers.append("public static void publish").append(methodName).append("(Pipe<").append(schemaClassName).append("> output");
		if (argsTemp.length()>0) {
			generatedProducers.append(", ").append(argsTemp);
		}
		generatedProducers.append(") {\n");
		  		
		generatedProducers.append("        PipeWriter.presumeWriteFragment(output, ").append(messageConst).append(");\n");
    	generatedProducers.append(bodyTemp);
    	generatedProducers.append("        PipeWriter.publishWrites(output);\n");

    	generatedProducers.append("}\n");
    	
	}
    
	private static void appendSwitchCase(StringBuilder result, String messageConstantName, String name) {
		result.append("            case ").append(messageConstantName).append(":\n");
		result.append("                consume"+name+"(input);\n");
		result.append("            break;\n");
	}

	private static void appendAssignmentCode(StringBuilder result, String constantName, int value, String comment) {
       
        result.append("public static final int ").append(constantName).append(" = ");
        Appendables.appendFixedHexDigits(result, value, 32).append("; //").append(comment).append("\n");
        
    }

}