package jadex.bridge; import jadex.commons.SUtil; import jadex.commons.Tuple; import jadex.commons.collection.MultiCollection; import jadex.commons.collection.SCollection; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Helper class for building error reports. */ public abstract class AbstractErrorReportBuilder { //-------- attributes -------- /** The unqualified (model/file) name. */ protected String name; /** The file name with path (optional). */ protected String filename; /** The element categories. */ protected String[] categories; /** The parse errors (tuple(elements) -> {error messages}). */ protected MultiCollection entries; /** The external documents for links in html error reports (id -> html text). */ protected Map externals; //-------- constructors -------- /** * Build the error based on the given entries (if any). * Entries represent error messages mapped by the path to the * xml element (as a tuple of stack elements). * @param name The unqualified (model/file) name. * @param filename The file name with path (optional). * @param categories The element categories. * @param entries The parse errors (tuple(stack elements) -> {error messages}). * @param externals The external documents for links in html error reports, if any (id -> html text). */ public AbstractErrorReportBuilder(String name, String filename, String[] categories, MultiCollection entries, Map externals) { this.name = name; this.filename = filename; this.categories = categories; this.entries = entries; this.externals = externals; } //-------- methods -------- /** * Build the error based on the given entries (if any). * Entries represent error messages mapped by the path to the * xml element (as a tuple of stack elements). * @return The error report. */ public IErrorReport buildErrorReport() { IErrorReport report = entries==null || entries.size()==0 ? null : new ErrorReport(generateErrorText(), generateErrorHTML(), externals); return report; } //-------- template methods -------- /** * Get the object of a path element * @param obj An item (entry) of a tuple in the multi collection. * @return The object corresponding to the entry. */ public abstract Object getPathElementObject(Object element); /** * Test if an object belongs to a category. * @param obj An item (entry) of a tuple in the multi collection. * @param category the category name. * @return True, when the object belongs to the category. */ public abstract boolean isInCategory(Object obj, String category); /** * Get the name of an object. * @param obj An object having an error. * @return A human readable name of the object. */ public abstract String getObjectName(Object obj); //-------- helper methods -------- /** * Get all invalid elements. */ protected Tuple[] getElements() { if(entries==null) return new Tuple[0]; else return (Tuple[])entries.getKeys(Tuple.class); } /** * Get the messages for a given element. */ protected String[] getMessages(Tuple path) { if(entries==null) { return SUtil.EMPTY_STRING_ARRAY; } else { Collection ret = entries.getCollection(path); return (String[])ret.toArray(new String[ret.size()]); } } /** * Generate a string representation of the report. */ protected String generateErrorText() { StringBuffer buf = new StringBuffer(); buf.append("Report for "); buf.append(name); buf.append("\n"); if(filename!=null) { buf.append("File: "); buf.append(filename); buf.append("\n"); } buf.append("\n"); Tuple[] elements = getElements(); for(int i=0; i<elements.length; i++) { Object obj = getObject(elements[i]); if(obj!=null) { String name = getObjectName(obj); name = name.replace("\n", " "); buf.append(name); buf.append(":\n"); } else { buf.append("Errors:\n"); } String[] messages = getMessages(elements[i]); for(int j=0; j<messages.length; j++) { buf.append("\t"); buf.append(messages[j]); buf.append("\n"); } } return SUtil.stripTags(buf.toString()); } /** * Generate an html representation of the report. */ protected String generateErrorHTML() { StringBuffer buf = new StringBuffer(); buf.append("<h3>Report for "); buf.append(name); buf.append("</h3>\n"); if(filename!=null) { buf.append("File: "); buf.append(filename); buf.append("\n"); } Set[] catels = new Set[categories.length]; Set excludes = new HashSet(); for(int i=0; i<categories.length; i++) { catels[i] = getOwnedElementErrors(categories[i]); excludes.addAll(catels[i]); } Set others = getOtherErrors(excludes); // Summaries. buf.append("<h4>Summary</h4>\n<ul>\n"); for(int i=0; i<categories.length; i++) { generateOverview(buf, categories[i], catels[i]); } generateOverview(buf, "Other element", others); buf.append("</ul>\n"); // Details. for(int i=0; i<categories.length; i++) { generateDetails(buf, categories[i], catels[i]); } generateDetails(buf, "Other element", others); return buf.toString(); } /** * Get elements of the given owner type, which have errors or contain elements with errors. */ protected Set getOwnedElementErrors(String category) { Set errors = SCollection.createLinkedHashSet(); Tuple[] elements = getElements(); for(int i=0; i<elements.length; i++) { boolean added = false; for(int j=0; !added && j<elements[i].getEntities().length; j++) { Object se = elements[i].getEntity(j); Object obj = getPathElementObject(se); if(obj!=null) { added = errors.contains(obj); if(!added && isInCategory(obj, category)) { errors.add(obj); added = true; } } } } return errors; } /** * Get other errors, not in the given tags. */ protected Set getOtherErrors(Set excludes) { Set errors = SCollection.createLinkedHashSet(); Tuple[] elements = getElements(); for(int i=0; i<elements.length; i++) { boolean excluded = false; for(int j=0; !excluded && j<elements[i].getEntities().length; j++) { Object se = elements[i].getEntity(j); Object obj = getPathElementObject(se); if(obj!=null) { excluded = excludes.contains(obj); } } if(!excluded) { Object obj = getObject(elements[i]); if(obj!=null && !errors.contains(obj)) errors.add(obj); } } return errors; } protected Object getObject(Tuple element) { Object ret = null; for(int j=element.getEntities().length-1; ret==null && j>=0; j--) { Object se = element.getEntity(j); Object obj = getPathElementObject(se); if(obj!=null) { ret = obj; } } return ret; } /** * Get all elements which have errors and are contained in the given element. */ protected Tuple[] getElementErrors(Object ancestor) { List errors = SCollection.createArrayList(); Tuple[] elements = getElements(); for(int i=0; i<elements.length; i++) { boolean added = errors.contains(elements[i]); for(int j=0; !added && j<elements[i].getEntities().length; j++) { Object se = elements[i].getEntity(j); Object obj = getPathElementObject(se); if(ancestor.equals(obj)) { errors.add(elements[i]); added = true; } } } return (Tuple[])errors.toArray(new Tuple[errors.size()]); } /** * Generate overview HTML code for the given elements. */ protected void generateOverview(StringBuffer buf, String type, Set elements) { if(!elements.isEmpty()) { buf.append("<li>"); buf.append(type); buf.append(" errors\n<ul>\n"); for(Iterator it=elements.iterator(); it.hasNext(); ) { Object obj = it.next(); String name = getObjectName(obj); buf.append("<li><a href=\"#"); buf.append(SUtil.makeConform(name)); buf.append("\">"); buf.append(SUtil.makeConform(name)); buf.append("</a> has errors.</li>\n"); } buf.append("</ul>\n</li>\n"); } } /** * Generate detail HTML code for the given elements. */ protected void generateDetails(StringBuffer buf, String type, Set elements) { if(!elements.isEmpty()) { buf.append("<h4>"); buf.append(type); buf.append(" details</h4>\n<ul>\n"); for(Iterator it=elements.iterator(); it.hasNext(); ) { Object obj = it.next(); String name = getObjectName(obj); buf.append("<li><a name=\""); buf.append(SUtil.makeConform(name)); buf.append("\">"); buf.append(SUtil.makeConform(name)); // Add name of configuration (hack???) // if(elements[i] instanceof IMConfigElement) // { // MElement owner = (MElement)elements[i]; // while(owner!=null && !(owner instanceof IMConfiguration)) // owner = owner.getOwner(); // if(owner!=null) // buf.append(" in "); // buf.append(SUtil.makeConform(""+owner)); // } buf.append("</a> errors:\n"); Tuple[] errors = getElementErrors(obj); buf.append("<dl>\n"); for(int j=0; j<errors.length; j++) { Object obj2 = getObject(errors[j]); if(!obj.equals(obj2)) { buf.append("<dt>"); buf.append(getObjectName(obj2)); buf.append("</dt>\n"); } // SourceLocation loc = errors[j].getSourceLocation(); // if(loc!=null) // { // buf.append(" ("); // buf.append(loc.getFilename()); // buf.append(": line "); // buf.append(loc.getLineNumber()); // buf.append(", column "); // buf.append(loc.getColumnNumber()); // buf.append(")"); // } String[] msgs = getMessages(errors[j]); buf.append("<dd>"); for(int k=0; k<msgs.length; k++) { buf.append(msgs[k]); buf.append("\n"); if(msgs.length>1 && k!=msgs.length-1) buf.append("<br>"); } buf.append("</dd>\n"); } buf.append("</dl>\n</li>\n"); } buf.append("</ul>\n"); } } }