├── settings.gradle ├── demo └── demo0001.txt ├── docs ├── annotated_enum.png ├── annotated_patch.png ├── annotated_source.png ├── annotated_trace.png └── annotated_talkback.png ├── src └── main │ ├── resources │ ├── Java_CodeDB.txt │ └── Java_Keywords.txt │ └── java │ └── io │ └── kuy │ └── infozilla │ ├── elements │ ├── patch │ │ ├── PatchHunk.java │ │ └── Patch.java │ ├── stacktrace │ │ ├── talkback │ │ │ ├── TalkbackTrace.java │ │ │ ├── TalkbackEntry.java │ │ │ └── TalkBack.java │ │ └── java │ │ │ └── StackTrace.java │ ├── enumeration │ │ └── Enumeration.java │ └── sourcecode │ │ └── java │ │ └── CodeRegion.java │ ├── filters │ ├── IFilter.java │ ├── FilterChain.java │ ├── FilterPatches.java │ ├── FilterTextRemover.java │ ├── FilterChainEclipsePS.java │ ├── FilterChainEclipse.java │ ├── FilterChainMozilla.java │ ├── FilterTalkBack.java │ ├── FilterSourceCodeJAVA.java │ ├── RelaxedPatchParser.java │ ├── PatchParser.java │ ├── FilterStackTraceJAVA.java │ └── FilterEnumeration.java │ ├── bugreports │ ├── Duplicate.java │ ├── Discussion.java │ ├── Message.java │ ├── MIME │ │ ├── MimeTyper.java │ │ └── MainMimeTyper.java │ ├── BugReport.java │ └── Attachment.java │ ├── datasources │ ├── bugzilladb │ │ └── ReportNotFoundException.java │ └── mbox │ │ └── MBox.java │ ├── helpers │ ├── RegExHelper.java │ ├── ConcurrentServices.java │ └── DataExportUtility.java │ └── cli │ └── Main.java ├── .gitignore ├── README.md └── LICENSE /settings.gradle: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/demo0001.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicbet/infozilla/HEAD/demo/demo0001.txt -------------------------------------------------------------------------------- /docs/annotated_enum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicbet/infozilla/HEAD/docs/annotated_enum.png -------------------------------------------------------------------------------- /docs/annotated_patch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicbet/infozilla/HEAD/docs/annotated_patch.png -------------------------------------------------------------------------------- /docs/annotated_source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicbet/infozilla/HEAD/docs/annotated_source.png -------------------------------------------------------------------------------- /docs/annotated_trace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicbet/infozilla/HEAD/docs/annotated_trace.png -------------------------------------------------------------------------------- /docs/annotated_talkback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicbet/infozilla/HEAD/docs/annotated_talkback.png -------------------------------------------------------------------------------- /src/main/resources/Java_CodeDB.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicbet/infozilla/HEAD/src/main/resources/Java_CodeDB.txt -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | 4 | # Ignore Gradle GUI config 5 | gradle-app.setting 6 | 7 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 8 | !gradle-wrapper.jar 9 | 10 | # Cache of project 11 | .gradletasknamecache 12 | 13 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 14 | # gradle/wrapper/gradle-wrapper.properties 15 | .settings 16 | demo 17 | bin 18 | .classpath 19 | .project 20 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/elements/patch/PatchHunk.java: -------------------------------------------------------------------------------- 1 | /** 2 | * PatchHunk.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.elements.patch; 6 | 7 | public class PatchHunk { 8 | private String text; 9 | 10 | public PatchHunk() { 11 | text = ""; 12 | } 13 | 14 | public PatchHunk(String text) { 15 | this.text = text; 16 | } 17 | 18 | public String getText() { 19 | return text; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/IFilter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * IFilter.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * This interface describes the method interface for every infoZilla Filter. 11 | * @author Nicolas Bettenburg 12 | * 13 | */ 14 | public interface IFilter { 15 | 16 | public List runFilter(String inputText); 17 | public String getOutputText(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/bugreports/Duplicate.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Duplicate.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.bugreports; 6 | 7 | 8 | public class Duplicate { 9 | 10 | private BugReport duplicateReport; 11 | private BugReport originalReport; 12 | 13 | public Duplicate(BugReport duplicateReport, BugReport originalReport) { 14 | super(); 15 | this.duplicateReport = duplicateReport; 16 | this.originalReport = originalReport; 17 | } 18 | 19 | public BugReport getDuplicateReport() { 20 | return duplicateReport; 21 | } 22 | 23 | public BugReport getOriginalReport() { 24 | return originalReport; 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/datasources/bugzilladb/ReportNotFoundException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * ReportNotFoundException.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.datasources.bugzilladb; 6 | 7 | public class ReportNotFoundException extends Exception { 8 | 9 | /** 10 | * Automatic generated serialization id 11 | */ 12 | private static final long serialVersionUID = 8766823313226301540L; 13 | 14 | public ReportNotFoundException() { 15 | } 16 | 17 | public ReportNotFoundException(String message) { 18 | super(message); 19 | } 20 | 21 | public ReportNotFoundException(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public ReportNotFoundException(String message, Throwable cause) { 26 | super(message, cause); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/helpers/RegExHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RegExHelper.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.helpers; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.regex.MatchResult; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | public class RegExHelper { 14 | 15 | public static Iterable findMatches(final Pattern p, 16 | CharSequence s) { 17 | List results = new ArrayList(); 18 | 19 | for (Matcher m = p.matcher(s); m.find();) { 20 | results.add(m.toMatchResult()); 21 | } 22 | return results; 23 | } 24 | 25 | public static String makeLinuxNewlines(String input) { 26 | String output = input.replaceAll("(([\r][\n])|([\r]))", "\n"); 27 | return output; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/FilterChain.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FilterChain.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.List; 8 | 9 | public interface FilterChain { 10 | 11 | /** 12 | * @return the outputText 13 | */ 14 | public String getOutputText(); 15 | 16 | /** 17 | * @return the patches 18 | */ 19 | public List getPatches(); 20 | 21 | /** 22 | * @return the traces 23 | */ 24 | public List getTraces(); 25 | 26 | /** 27 | * @return the regions 28 | */ 29 | public List getRegions(); 30 | 31 | /** 32 | * @return the enumerations 33 | */ 34 | public List getEnumerations(); 35 | 36 | /** 37 | * @param inputText the inputText to set 38 | */ 39 | public void setInputText(String inputText); 40 | 41 | /** 42 | * @return the inputText 43 | */ 44 | public String getInputText(); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/helpers/ConcurrentServices.java: -------------------------------------------------------------------------------- 1 | /** 2 | * ConcurrentServices.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.helpers; 6 | 7 | import java.util.concurrent.Callable; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.Future; 11 | 12 | 13 | public class ConcurrentServices { 14 | 15 | private static ExecutorService executorService = Executors.newCachedThreadPool(); 16 | 17 | 18 | public static Future submit(Callable task) { 19 | return executorService.submit(task); 20 | } 21 | 22 | public static Future submit(Runnable task) { 23 | return executorService.submit(task); 24 | } 25 | 26 | public static Future submit(Runnable task, T result) { 27 | return executorService.submit(task, result); 28 | } 29 | 30 | public static void shutdown() { 31 | executorService.shutdown(); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/main/resources/Java_Keywords.txt: -------------------------------------------------------------------------------- 1 | abstract // MODIFIER 2 | continue 3 | for // for WS* ( EXP* ) WS* { EXP* } 4 | new 5 | switch // switch WS* ( ID ) WS* { EXP* } 6 | assert // assert WS* ( EXP ) 7 | default // default: EXP* WS*; WS* break WS*; 8 | goto // not used 9 | package // package WS* DOTID WS*; 10 | synchronized // MODIFIER 11 | boolean 12 | do // do WS* { EXP* } WS* while WS* ( EXP ) WS*; 13 | if // if WS* ( EXP ) WS* { EXP* } 14 | private 15 | this 16 | break 17 | double 18 | implements 19 | protected // MODIFIER 20 | throw 21 | byte 22 | else // else WS* { EXP* } 23 | import // import WS* DOTID WS*; 24 | public // MODIFIER 25 | throws 26 | case // case WS* VAL: WS* EXP WS*; WS* break WS*; 27 | enum 28 | instanceof 29 | return // return WS* ( EXP ) WS*; 30 | transient 31 | catch // catch WS* { EXP* } 32 | extends 33 | int 34 | short 35 | try // try WS* { EXP* } 36 | char 37 | final 38 | interface 39 | static 40 | void 41 | class // MODIFIER class WS* ID WS* [EXTENSION] WS* { EXP* } 42 | finally 43 | long 44 | strictfp 45 | volatile 46 | const 47 | float 48 | native 49 | super // super WS* ( EXP ) WS*; 50 | while -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/elements/stacktrace/talkback/TalkbackTrace.java: -------------------------------------------------------------------------------- 1 | /** 2 | * TalkbackTrace.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.elements.stacktrace.talkback; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * A Mozilla TALKBACK� Stack Trace. Warning: the Talkback format was reverse engineered 12 | * and might not fully comply with the full specifications! 13 | * @author Nicolas Bettenburg 14 | * 15 | */ 16 | public class TalkbackTrace { 17 | private List entries; 18 | 19 | public TalkbackTrace(List entries) { 20 | super(); 21 | this.entries = entries; 22 | } 23 | 24 | public TalkbackTrace() { 25 | super(); 26 | this.entries = new ArrayList(); 27 | } 28 | 29 | public List getEntries() { 30 | return entries; 31 | } 32 | 33 | public void setEntries(List entries) { 34 | this.entries = entries; 35 | } 36 | 37 | public String toString() { 38 | if (entries != null) { 39 | StringBuilder sb = new StringBuilder(); 40 | for (TalkbackEntry entry : entries ) { 41 | sb.append(entry.toString() + System.getProperty("line.separator")); 42 | } 43 | return (sb.toString()); 44 | } else { 45 | return (this.getClass().getName() + " " + this.hashCode()); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/elements/stacktrace/talkback/TalkbackEntry.java: -------------------------------------------------------------------------------- 1 | /** 2 | * TalkbackEntry.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.elements.stacktrace.talkback; 6 | 7 | public class TalkbackEntry { 8 | // Types definitions 9 | public final static int CLASSMETHODLINE = 1; 10 | public final static int METHODCALLLINE = 2; 11 | public final static int METHODLINE = 3; 12 | public final static int LIBRARYLINE = 4; 13 | public final static int ADDRESSLINE = 5; 14 | 15 | // The name 16 | private String name; 17 | 18 | // The location 19 | private String location; 20 | 21 | // The type 22 | private int type = 0; 23 | 24 | public TalkbackEntry(String name, String location, int type) { 25 | super(); 26 | this.name = name; 27 | this.location = location; 28 | this.type = type; 29 | } 30 | 31 | public String getName() { 32 | return name.trim(); 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | } 38 | 39 | public String getLocation() { 40 | return location.trim(); 41 | } 42 | 43 | public void setLocation(String location) { 44 | this.location = location; 45 | } 46 | 47 | public int getType() { 48 | return type; 49 | } 50 | 51 | public void setType(int type) { 52 | this.type = type; 53 | } 54 | 55 | public String toString() { 56 | return (name.trim() + " (" + location.trim() + ")"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/bugreports/Discussion.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Discussion.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.bugreports; 6 | 7 | import java.sql.Timestamp; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.PriorityQueue; 11 | 12 | 13 | public class Discussion { 14 | 15 | private PriorityQueue messageQueue = null; 16 | 17 | public Discussion() { 18 | messageQueue = new PriorityQueue(); 19 | } 20 | 21 | public void addMessage(Message m) { 22 | messageQueue.add(m); 23 | } 24 | 25 | public List getMessages() { 26 | // Copy Messages 27 | PriorityQueue tmpQueue = new PriorityQueue(messageQueue); 28 | 29 | List msgList = new ArrayList(); 30 | while(! tmpQueue.isEmpty()) { 31 | Message cmd = tmpQueue.poll(); 32 | msgList.add(cmd); 33 | } 34 | return msgList; 35 | } 36 | 37 | public List getMessages(Timestamp untilDate) { 38 | // Copy Messages 39 | PriorityQueue tmpQueue = new PriorityQueue(messageQueue); 40 | 41 | List msgList = new ArrayList(); 42 | while(! tmpQueue.isEmpty()) { 43 | Message m = tmpQueue.poll(); 44 | if (m.getTime().before(untilDate)) { 45 | msgList.add(m); 46 | } 47 | } 48 | return msgList; 49 | } 50 | 51 | public Message getDescription() { 52 | return messageQueue.peek(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/elements/stacktrace/talkback/TalkBack.java: -------------------------------------------------------------------------------- 1 | /** 2 | * TalkBack.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.elements.stacktrace.talkback; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * Class representing a Mozilla Talkback Stacktrace Entry 12 | */ 13 | public class TalkBack { 14 | 15 | private String incident_id; 16 | private Map fields = null; 17 | private TalkbackTrace trace = null; 18 | 19 | public TalkBack(String incident_id) { 20 | this.incident_id = incident_id; 21 | this.fields = new HashMap(); 22 | this.trace = new TalkbackTrace(); 23 | } 24 | 25 | public TalkBack(String incident_id, Map fields, TalkbackTrace trace) { 26 | super(); 27 | this.incident_id = incident_id; 28 | this.fields = fields; 29 | this.trace = trace; 30 | } 31 | 32 | public void setField(String name, String value) { 33 | this.fields.put(name, value); 34 | } 35 | 36 | public String getField(String name) { 37 | if (this.fields.containsKey(name)) { 38 | return fields.get(name); 39 | } else { 40 | return ""; 41 | } 42 | } 43 | 44 | public String getIncident_id() { 45 | return incident_id; 46 | } 47 | 48 | public void setIncident_id(String incident_id) { 49 | this.incident_id = incident_id; 50 | } 51 | 52 | public TalkbackTrace getTrace() { 53 | return trace; 54 | } 55 | 56 | public void setTrace(TalkbackTrace trace) { 57 | this.trace = trace; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/FilterPatches.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FilterPatches.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.List; 8 | 9 | import io.kuy.infozilla.elements.patch.Patch; 10 | 11 | /** 12 | * This Filter class acts as an interface for the PatchParser class 13 | * that has to be instantiated before using it. 14 | * @author Nicolas Bettenburg 15 | * 16 | */ 17 | public class FilterPatches implements IFilter { 18 | 19 | private FilterTextRemover textRemover; 20 | private boolean relaxed = false; 21 | 22 | public FilterPatches() { 23 | } 24 | 25 | /** 26 | * Filter a list of {@link Patch}es from a text {@link s} 27 | * @param text the text we should look for patches inside 28 | * @return a List of {@link Patch}es. 29 | */ 30 | private List getPatches(String text) { 31 | // Setup Helper classes 32 | textRemover = new FilterTextRemover(text); 33 | 34 | // Find Patches 35 | List foundPatches = null; 36 | if ( isRelaxed() ) { 37 | RelaxedPatchParser pp = new RelaxedPatchParser(); 38 | foundPatches = pp.parseForPatches(text); 39 | } else { 40 | PatchParser pp = new PatchParser(); 41 | foundPatches = pp.parseForPatches(text); 42 | } 43 | 44 | // Filter them out 45 | for (Patch patch : foundPatches) { 46 | textRemover.markForDeletion(patch.getStartPosition(), patch.getEndPosition()); 47 | } 48 | return foundPatches; 49 | } 50 | 51 | 52 | public String getOutputText() { 53 | return textRemover.doDelete(); 54 | } 55 | 56 | public List runFilter(String inputText) { 57 | return getPatches(inputText); 58 | } 59 | 60 | public boolean isRelaxed() { 61 | return relaxed; 62 | } 63 | 64 | public void setRelaxed(boolean relaxed) { 65 | this.relaxed = relaxed; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/elements/enumeration/Enumeration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Enumeration.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.elements.enumeration; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * This class encapsulates an Enumeration as created by FilterEnumerations filter. 12 | * Although we are using this for now to discover "Steps to Reproduce", a user should 13 | * probably subclass from Enumeration and add custom heuristics. 14 | * @author Nicolas Bettenburg 15 | * 16 | */ 17 | public class Enumeration { 18 | 19 | private List enumeration_items; 20 | private int start; 21 | private int end; 22 | private int enumStart; 23 | private int enumEnd; 24 | 25 | public int getEnumStart() { 26 | return enumStart; 27 | } 28 | 29 | public void setEnumStart(int enumStart) { 30 | this.enumStart = enumStart; 31 | } 32 | 33 | public int getEnumEnd() { 34 | return enumEnd; 35 | } 36 | 37 | public void setEnumEnd(int enumEnd) { 38 | this.enumEnd = enumEnd; 39 | } 40 | 41 | public Enumeration( List items, int start, int end) { 42 | this.start = start; 43 | this.end = end; 44 | this.enumeration_items = items; 45 | } 46 | 47 | public Enumeration() { 48 | this.enumeration_items = new ArrayList(); 49 | } 50 | 51 | public int getStart() { 52 | return start; 53 | } 54 | 55 | public void setStart(int start) { 56 | this.start = start; 57 | } 58 | 59 | public int getEnd() { 60 | return end; 61 | } 62 | 63 | public void setEnd(int end) { 64 | this.end = end; 65 | } 66 | 67 | public List getEnumeration_items() { 68 | return enumeration_items; 69 | } 70 | 71 | public void setEnumeration_items(List enumeration_items) { 72 | this.enumeration_items = enumeration_items; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/bugreports/Message.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Message.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.bugreports; 6 | 7 | import java.sql.Timestamp; 8 | 9 | /** 10 | * The Message Class describes one Message in the complete Discussion of a Bug Report. 11 | * @author Nicolas Bettenburg 12 | */ 13 | public class Message implements Comparable{ 14 | 15 | private String contact; 16 | private Timestamp time; 17 | private String text; 18 | 19 | /** 20 | * Overloaded Constructor 21 | * @param contact the person that submitted the message. 22 | * @param time the time and date, the message was submitted as java.util.Timestamp. 23 | * @param text the message text. 24 | */ 25 | public Message(String contact, Timestamp time, String text) { 26 | super(); 27 | this.contact = contact; 28 | this.time = time; 29 | this.text = text; 30 | } 31 | 32 | /** 33 | * This method is used to get the contact information of the person, who wrote the message. 34 | * @return a String describing the author of the message. 35 | */ 36 | public String getContact() { 37 | return contact; 38 | } 39 | 40 | /** 41 | * This method is used to set the name or email address of the message's author 42 | * @param contact a String containing the name or the address of the author. 43 | */ 44 | public void setContact(String contact) { 45 | this.contact = contact; 46 | } 47 | 48 | 49 | /** 50 | * This method is used to get information about the time at which this message was submitted. 51 | * @return a Timestamp of message creation in the bug database. 52 | */ 53 | public Timestamp getTime() { 54 | return time; 55 | } 56 | 57 | 58 | 59 | public void setTime(Timestamp time) { 60 | this.time = time; 61 | } 62 | 63 | 64 | 65 | public String getText() { 66 | return text; 67 | } 68 | 69 | 70 | 71 | public void setText(String text) { 72 | this.text = text; 73 | } 74 | 75 | 76 | 77 | public int compareTo(Message that) { 78 | if (this.getTime().after(that.getTime())) { 79 | return 1; 80 | } else if (this.getTime().before(that.getTime())) { 81 | return -1; 82 | } else { 83 | return 0; 84 | } 85 | } 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/elements/sourcecode/java/CodeRegion.java: -------------------------------------------------------------------------------- 1 | /** 2 | * CodeRegion.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.elements.sourcecode.java; 6 | 7 | /** 8 | * The CodeRegion class represents source code structural elements. 9 | * Each CodeRegion records the start and end 10 | * positions in the original input text, as well as the type of the source code element detected. 11 | * @author Nicolas Bettenburg 12 | * @see de.unisb.cs.st.infoZilla.Filters.FilterSourceCodeJAVA 13 | * @see de.unisb.cs.st.infoZilla.Filters.FilterChain 14 | * @see de.unisb.cs.st.infoZilla.Ressources.Java_CodeDB.txt 15 | * @see de.unisb.cs.st.infoZilla.Ressources.Java_Keywords.txt 16 | */ 17 | public class CodeRegion implements Comparable{ 18 | 19 | /** Stores the start position of the source code region in the original input text */ 20 | public int start=0; 21 | 22 | /** Stores the end position of the source code region in the original input text */ 23 | public int end=0; 24 | 25 | /** Stores the textual representation of the source code region */ 26 | public String text; 27 | 28 | /** Stores the type of source code region as defined in de.unisb.cs.st.infoZilla.Ressources.Java_CodeDB.txt */ 29 | public String keyword; 30 | 31 | /** 32 | * Standard Constructor 33 | * @param start start position of code region 34 | * @param end end position of code region 35 | * @param keyword type of code region 36 | * @param text textual representation 37 | */ 38 | public CodeRegion(int start, int end, String keyword, String text) { 39 | super(); 40 | this.start = start; 41 | this.end = end; 42 | this.keyword = keyword; 43 | this.text = text; 44 | } 45 | 46 | /** 47 | * Copy Constructor 48 | * @param that another CodeRegion object to copy from. 49 | */ 50 | public CodeRegion(CodeRegion that) { 51 | super(); 52 | this.start = Integer.valueOf(that.start); 53 | this.end = Integer.valueOf(that.end); 54 | this.keyword = new String(that.keyword); 55 | this.text = new String(that.text); 56 | } 57 | 58 | 59 | public int compareTo(CodeRegion that) { 60 | if (this.start < that.start) return -1; 61 | if (this.start > that.start) return +1; 62 | return 0; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/FilterTextRemover.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FilterTextRemover.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * The FilterTextRemover class is used to cut out the structural element 12 | * from the complete input text. Given a list of (possibly overlapping) cut points, 13 | * the class calculates the longest consecutive parts and marks them for deletion 14 | * through a character-wise bit mask. 15 | * @author Nicolas Bettenburg. 16 | * 17 | */ 18 | public class FilterTextRemover { 19 | 20 | public class TextCutPoint { 21 | private int start; 22 | public int getStart() { 23 | return start; 24 | } 25 | public void setStart(int start) { 26 | this.start = start; 27 | } 28 | public int getEnd() { 29 | return end; 30 | } 31 | public void setEnd(int end) { 32 | this.end = end; 33 | } 34 | private int end; 35 | public TextCutPoint(int start, int end) { 36 | super(); 37 | this.start = start; 38 | this.end = end; 39 | } 40 | } 41 | 42 | private String originalText = ""; 43 | private boolean[] deletionMask; 44 | private List cutPoints; 45 | 46 | public FilterTextRemover(String originalText) { 47 | // Set the Text 48 | this.originalText = originalText; 49 | 50 | // Create a deletion mask of appropriate size 51 | deletionMask = new boolean[originalText.length()]; 52 | 53 | // Initialize the Deletion Mask with false (=do not delete) 54 | for (int i=0; i < deletionMask.length; i++) { 55 | deletionMask[i] = false; 56 | } 57 | 58 | cutPoints = new ArrayList(); 59 | } 60 | 61 | public void markForDeletion(int start, int end) { 62 | cutPoints.add(new TextCutPoint(start, end)); 63 | if (start >= 0 && end <= deletionMask.length) { 64 | for (int i = start; i < end; i++) { 65 | deletionMask[i] = true; 66 | } 67 | } else { 68 | System.err.println("Warning! Trying to Delete out of Bounds: " + start + " until " + end + " but bounds are 0:" + deletionMask.length); 69 | System.err.println("Will not mark for deletion!"); 70 | } 71 | } 72 | 73 | 74 | public String doDelete() { 75 | StringBuilder myStringBuilder = new StringBuilder(); 76 | 77 | for (int i = 0; i < originalText.length(); i++) { 78 | if (!deletionMask[i]) 79 | myStringBuilder.append(originalText.charAt(i)); 80 | } 81 | return myStringBuilder.toString(); 82 | } 83 | 84 | public String getText() { 85 | return originalText; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/datasources/mbox/MBox.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MBox.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.datasources.mbox; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.File; 9 | import java.io.FileReader; 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | @Deprecated 15 | public class MBox { 16 | 17 | String filename; 18 | List messages = null; 19 | 20 | /** 21 | * Nested class describing a Message in the MBOX file 22 | * @author nicbet 23 | * 24 | */ 25 | public class Message { 26 | private String from; 27 | private String text; 28 | public Message(String from, String text) { 29 | super(); 30 | this.from = from; 31 | this.text = text; 32 | } 33 | public String getFrom() { 34 | return from; 35 | } 36 | public void setFrom(String from) { 37 | this.from = from; 38 | } 39 | public String getText() { 40 | return text; 41 | } 42 | public void setText(String text) { 43 | this.text = text; 44 | } 45 | } 46 | 47 | /** 48 | * Read the messages from the MBOX file described by this class 49 | * @return a List of Messages 50 | */ 51 | public List readMessages() { 52 | messages = new ArrayList(); 53 | 54 | String mbox_text = ""; 55 | 56 | // Read the file 57 | try { 58 | File f = new File(filename); 59 | BufferedReader reader = new BufferedReader(new FileReader(f)); 60 | StringBuilder pb = new StringBuilder(); 61 | String buffer = ""; 62 | while ((buffer = reader.readLine()) != null) { 63 | pb.append(buffer + System.getProperty("line.separator")); 64 | } 65 | 66 | mbox_text = pb.toString(); 67 | reader.close(); 68 | } catch (IOException e) { 69 | System.out.println("Error opening the MBOX file: " + e.getMessage()); 70 | } 71 | 72 | // Split the mbox text into messages 73 | String[] pMessages = mbox_text.split("[\n\r]{2}From"); 74 | 75 | 76 | // Make messages 77 | for (String pMessage : pMessages) { 78 | Message aMessage = new Message("",pMessage); 79 | messages.add(aMessage); 80 | } 81 | return messages; 82 | } 83 | 84 | /** 85 | * Standard Constructor 86 | * @param filename the path and filename of the MBOX file 87 | */ 88 | public MBox(String filename) { 89 | super(); 90 | this.filename = filename; 91 | } 92 | 93 | public String getFilename() { 94 | return filename; 95 | } 96 | 97 | public void setFilename(String filename) { 98 | this.filename = filename; 99 | } 100 | 101 | public List getMessages() { 102 | return messages; 103 | } 104 | 105 | public void setMessages(List messages) { 106 | this.messages = messages; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/elements/patch/Patch.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Patch.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.elements.patch; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.regex.MatchResult; 10 | import java.util.regex.Pattern; 11 | 12 | import io.kuy.infozilla.helpers.RegExHelper; 13 | 14 | public class Patch { 15 | private String index = ""; 16 | private String originalFile = ""; 17 | private String modifiedFile = ""; 18 | private String header = ""; 19 | private int startPosition; 20 | private int endPosition; 21 | 22 | private List hunks; 23 | 24 | public Patch() { 25 | hunks = new ArrayList(); 26 | startPosition = 0; 27 | endPosition = 0; 28 | } 29 | 30 | public Patch(int s, int e) { 31 | hunks = new ArrayList(); 32 | startPosition = s; 33 | endPosition = e; 34 | } 35 | 36 | public Patch(int s) { 37 | hunks = new ArrayList(); 38 | startPosition = s; 39 | } 40 | 41 | public void addHunk(PatchHunk hunk) { 42 | hunks.add(hunk); 43 | } 44 | 45 | public String getIndex() { 46 | if (index.length() > 7) 47 | return (index.substring(7, index.length())); 48 | else 49 | return index; 50 | } 51 | 52 | public void setIndex(String index) { 53 | this.index = index; 54 | } 55 | 56 | public String PlusMinusLineToFilename(String input) { 57 | String temp = input; 58 | String pmreg = "([-]{3}|[+]{3})([ \\r\\n\\t](.*?)[ \\t])"; 59 | for (MatchResult r : RegExHelper.findMatches(Pattern.compile(pmreg, Pattern.MULTILINE ), input)) { 60 | if (r.groupCount() > 1) 61 | temp = r.group(2).trim(); 62 | } 63 | return temp; 64 | } 65 | 66 | public String getOriginalFile() { 67 | return PlusMinusLineToFilename(originalFile); 68 | } 69 | 70 | public void setOriginalFile(String originalFile) { 71 | this.originalFile = originalFile; 72 | } 73 | 74 | public String getModifiedFile() { 75 | return PlusMinusLineToFilename(modifiedFile); 76 | } 77 | 78 | public void setModifiedFile(String modifiedFile) { 79 | this.modifiedFile = modifiedFile; 80 | } 81 | 82 | public List getHunks() { 83 | return hunks; 84 | } 85 | 86 | @Override 87 | public String toString() { 88 | String s = ""; 89 | String lineSep = System.getProperty("line.separator"); 90 | s = s + index + lineSep; 91 | s = s + "ORIGINAL=" + getOriginalFile() + lineSep; 92 | s = s + "MODIFIED=" + getModifiedFile() + lineSep; 93 | s = s + "#HUNKS=" + Integer.valueOf(hunks.size()) + lineSep; 94 | return s; 95 | } 96 | 97 | public String getHeader() { 98 | return header; 99 | } 100 | 101 | public void setHeader(String header) { 102 | this.header = header; 103 | } 104 | 105 | public int getStartPosition() { 106 | return startPosition; 107 | } 108 | 109 | public void setStartPosition(int startPosition) { 110 | this.startPosition = startPosition; 111 | } 112 | 113 | public int getEndPosition() { 114 | return endPosition; 115 | } 116 | 117 | public void setEndPosition(int endPosition) { 118 | this.endPosition = endPosition; 119 | } 120 | 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/elements/stacktrace/java/StackTrace.java: -------------------------------------------------------------------------------- 1 | /** 2 | * StackTrace.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.elements.stacktrace.java; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * Class representing a Stack Trace (in Java 1.4, 1.5, 1.6) 12 | * @author Nicolas Bettenburg 13 | */ 14 | public class StackTrace { 15 | 16 | private String exception; 17 | private String reason; 18 | private List frames; 19 | private boolean isCause; 20 | private int traceStart; 21 | private int traceEnd; 22 | 23 | public int getTraceStart() { 24 | return traceStart; 25 | } 26 | 27 | public void setTraceStart(int traceStart) { 28 | this.traceStart = traceStart; 29 | } 30 | 31 | public int getTraceEnd() { 32 | return traceEnd; 33 | } 34 | 35 | public void setTraceEnd(int traceEnd) { 36 | this.traceEnd = traceEnd; 37 | } 38 | 39 | /** 40 | * Default Constructor - sets attributes to predefined values. 41 | */ 42 | public StackTrace() { 43 | this.traceStart = 0; 44 | this.traceEnd = 0; 45 | this.exception = "Not specified"; 46 | this.reason = "No reason given"; 47 | this.frames = new ArrayList(); 48 | this.isCause = false; 49 | } 50 | 51 | /** 52 | * Overloaded Constructor 53 | * @param exception The Exception resulting in this Stack Trace. 54 | * @param reason The Reason given in the Exception. 55 | * @param frames The list of locations the trace originated from. 56 | */ 57 | public StackTrace(String exception, String reason, List frames) { 58 | this.traceStart = 0; 59 | this.traceEnd = 0; 60 | this.exception = exception; 61 | this.reason = reason; 62 | this.frames = frames; 63 | this.isCause = false; 64 | } 65 | 66 | 67 | /** 68 | * Overloaded Constructor 69 | * @param exception The Reason given in the Exception. 70 | * @param reason The list of locations the trace originated from. 71 | */ 72 | public StackTrace(String exception, String reason) { 73 | this.traceStart = 0; 74 | this.traceEnd = 0; 75 | this.exception = exception; 76 | this.reason = reason; 77 | this.frames = new ArrayList(); 78 | this.isCause = false; 79 | } 80 | 81 | /** 82 | * Getter for the Exception 83 | * @return the exception this Stack Trace originated from 84 | */ 85 | public String getException() { 86 | return exception; 87 | } 88 | 89 | /** 90 | * Getter for the Reason 91 | * @return the reason for this Stack Trace stated in the first Exception. (removed trailing :) 92 | */ 93 | public String getReason() { 94 | if (reason.startsWith(": ") || reason.startsWith(" :")) 95 | return reason.substring(2); 96 | else 97 | return reason; 98 | } 99 | 100 | /** 101 | * Getter for the Trace Locations 102 | * @return A list of locations where the trace originated from. 103 | */ 104 | public List getFrames() { 105 | return(frames); 106 | } 107 | 108 | /** 109 | * Set the Stack Trace to be a cause or not 110 | * @param isCause A boolean value if this stack trace is a cause 111 | */ 112 | public void setCause(boolean isCause) { 113 | this.isCause = isCause; 114 | } 115 | 116 | /** 117 | * Check whether this Stack Trace is a cause or not 118 | * @return A boolean value indicating if the Stack Trace is a cause. 119 | */ 120 | public boolean isCause() { 121 | return isCause; 122 | } 123 | 124 | /** 125 | * Joins the text of all Frames in frames 126 | * @return a single String with all frames concattenated 127 | */ 128 | public String getFramesText() { 129 | String framesText = ""; 130 | for (String frame : frames) { 131 | framesText = framesText + "at " + frame + System.getProperty("line.separator"); 132 | } 133 | return framesText; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/bugreports/MIME/MimeTyper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MimeTyper.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.bugreports.MIME; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.File; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.io.InputStreamReader; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | import io.kuy.infozilla.bugreports.Attachment; 17 | import io.kuy.infozilla.datasources.bugzilladb.DatabaseConnector; 18 | 19 | public class MimeTyper { 20 | 21 | public static Map discoverMimeTypesOfBugReport(int bug_id, DatabaseConnector dbc) { 22 | Map mimeTypes = new HashMap(); 23 | //System.out.println("-- Processing Bug Report " + bug_id); 24 | try { 25 | List attachments = dbc.getRAWAttachmentsFor(bug_id); 26 | for (Attachment attachment : attachments) { 27 | String attmtFileName = attachment.getFilename(); 28 | int lastSeparator = attmtFileName.lastIndexOf("."); 29 | String prefix = ""; 30 | String suffix = ""; 31 | if (lastSeparator > 0) { 32 | prefix = attmtFileName.substring(0, lastSeparator); 33 | suffix = attmtFileName.substring(lastSeparator, attmtFileName.length()); 34 | } else { 35 | prefix = attmtFileName; 36 | } 37 | 38 | prefix = prefix.replaceAll("[ \\t\\n\\r]", "_"); 39 | suffix = suffix.replaceAll("[ \\t\\n\\r]", "_"); 40 | 41 | File attmtFile = File.createTempFile(attachment.getAttachmentID() + "_" + prefix, suffix); 42 | //System.out.println("-- Creating Temporary File: " + attmtFile.getCanonicalPath()); 43 | FileOutputStream fos = new FileOutputStream(attmtFile); 44 | fos.write(attachment.getData()); 45 | fos.close(); 46 | String command = "file -b -i -n "; 47 | if (System.getProperty("os.name").contains("Mac OS")) 48 | command = "file -b -I -n "; 49 | System.out.println("-- " + command + attmtFile.getCanonicalPath()); 50 | String xx = invokeCommand(command + attmtFile.getCanonicalPath()); 51 | //System.out.println("--" + xx); 52 | mimeTypes.put(attachment.getAttachmentID(), xx); 53 | attmtFile.delete(); 54 | } 55 | } catch (Exception e) { 56 | System.err.println("Error Guessing Mime Types for Attachments of bug " + bug_id); 57 | System.out.println(e.getMessage()); 58 | e.printStackTrace(); 59 | } 60 | 61 | return mimeTypes; 62 | } 63 | 64 | public static String invokeCommand(String command) { 65 | String s; 66 | StringBuilder results = new StringBuilder(); 67 | try { 68 | 69 | // run the Unix "ps -ef" command 70 | // using the Runtime exec method: 71 | Process p = Runtime.getRuntime().exec(command); 72 | 73 | BufferedReader stdInput = new BufferedReader(new 74 | InputStreamReader(p.getInputStream())); 75 | 76 | BufferedReader stdError = new BufferedReader(new 77 | InputStreamReader(p.getErrorStream())); 78 | 79 | // read the output from the command 80 | while ((s = stdInput.readLine()) != null) { 81 | results.append(s); 82 | results.append(System.getProperty("line.separator")); 83 | } 84 | 85 | // read any errors from the attempted command 86 | while ((s = stdError.readLine()) != null) { 87 | results.append(s); 88 | results.append(System.getProperty("line.separator")); 89 | } 90 | 91 | p.destroy(); 92 | } 93 | catch (IOException e) { 94 | System.out.println("Error executing command " + command + " :"); 95 | e.printStackTrace(); 96 | } 97 | return results.toString(); 98 | } 99 | 100 | 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/FilterChainEclipsePS.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FilterChainEclipsePS.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import io.kuy.infozilla.elements.enumeration.Enumeration; 11 | import io.kuy.infozilla.elements.patch.Patch; 12 | import io.kuy.infozilla.elements.sourcecode.java.CodeRegion; 13 | import io.kuy.infozilla.elements.stacktrace.java.StackTrace; 14 | import io.kuy.infozilla.helpers.RegExHelper; 15 | 16 | /** 17 | * Class for runnning the complete filter chain on an eclipse input and gathering the results 18 | * @author Nicolas Bettenburg 19 | * 20 | */ 21 | public class FilterChainEclipsePS implements FilterChain { 22 | // Private Attributes 23 | private FilterPatches patchFilter; 24 | private FilterStackTraceJAVA stacktraceFilter; 25 | private FilterSourceCodeJAVA sourcecodeFilter; 26 | private FilterEnumeration enumFilter; 27 | 28 | private String inputText = ""; 29 | private String outputText = ""; 30 | 31 | private List patches; 32 | private List traces; 33 | private List regions; 34 | private List enumerations; 35 | 36 | // Constructor runs the experiments 37 | public FilterChainEclipsePS(String inputText) { 38 | patchFilter = new FilterPatches(); 39 | stacktraceFilter = new FilterStackTraceJAVA(); 40 | sourcecodeFilter = new FilterSourceCodeJAVA(FilterChainEclipsePS.class.getResource("/Java_CodeDB.txt")); 41 | enumFilter = new FilterEnumeration(); 42 | 43 | this.inputText = RegExHelper.makeLinuxNewlines(inputText); 44 | this.outputText = this.inputText; 45 | 46 | patches = patchFilter.runFilter(outputText); 47 | outputText = patchFilter.getOutputText(); 48 | 49 | traces = stacktraceFilter.runFilter(outputText); 50 | outputText = stacktraceFilter.getOutputText(); 51 | 52 | regions = new ArrayList(); 53 | enumerations = new ArrayList(); 54 | } 55 | 56 | public FilterChainEclipsePS(String inputText, boolean runPatches, boolean runTraces, boolean runSource, boolean runEnums) { 57 | patchFilter = new FilterPatches(); 58 | stacktraceFilter = new FilterStackTraceJAVA(); 59 | sourcecodeFilter = new FilterSourceCodeJAVA(FilterChainEclipsePS.class.getResource("/Java_CodeDB.txt")); 60 | enumFilter = new FilterEnumeration(); 61 | 62 | this.inputText = RegExHelper.makeLinuxNewlines(inputText); 63 | this.outputText = this.inputText; 64 | 65 | if (runPatches) patches = patchFilter.runFilter(outputText); 66 | else patches = new ArrayList(); 67 | 68 | outputText = patchFilter.getOutputText(); 69 | 70 | if (runTraces) traces = stacktraceFilter.runFilter(outputText); 71 | else traces = new ArrayList(); 72 | 73 | outputText = stacktraceFilter.getOutputText(); 74 | 75 | if (runSource) { 76 | regions = sourcecodeFilter.runFilter(outputText); 77 | outputText = sourcecodeFilter.getOutputText(); 78 | } 79 | else regions = new ArrayList(); 80 | 81 | if (runEnums) { 82 | enumerations = enumFilter.runFilter(outputText); 83 | // The output of the filter chain 84 | outputText = sourcecodeFilter.getOutputText(); 85 | } 86 | else enumerations = new ArrayList(); 87 | 88 | 89 | } 90 | 91 | /** 92 | * @return the outputText 93 | */ 94 | public String getOutputText() { 95 | return outputText; 96 | } 97 | 98 | /** 99 | * @return the patches 100 | */ 101 | public List getPatches() { 102 | return patches; 103 | } 104 | 105 | /** 106 | * @return the traces 107 | */ 108 | public List getTraces() { 109 | return traces; 110 | } 111 | 112 | /** 113 | * @return the regions 114 | */ 115 | public List getRegions() { 116 | return regions; 117 | } 118 | 119 | /** 120 | * @return the enumerations 121 | */ 122 | public List getEnumerations() { 123 | return enumerations; 124 | } 125 | 126 | /** 127 | * @param inputText the inputText to set 128 | */ 129 | public void setInputText(String inputText) { 130 | this.inputText = inputText; 131 | } 132 | 133 | /** 134 | * @return the inputText 135 | */ 136 | public String getInputText() { 137 | return inputText; 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/cli/Main.java: -------------------------------------------------------------------------------- 1 | package io.kuy.infozilla.cli; 2 | 3 | import java.io.File; 4 | import java.nio.charset.Charset; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.StandardOpenOption; 8 | import java.sql.Timestamp; 9 | import java.util.Date; 10 | 11 | import org.jdom.Document; 12 | import org.jdom.Element; 13 | import org.jdom.output.Format; 14 | import org.jdom.output.XMLOutputter; 15 | 16 | import io.kuy.infozilla.filters.FilterChainEclipse; 17 | import io.kuy.infozilla.helpers.DataExportUtility; 18 | import picocli.CommandLine; 19 | import picocli.CommandLine.Command; 20 | import picocli.CommandLine.Option; 21 | import picocli.CommandLine.Parameters; 22 | 23 | /** 24 | * Main 25 | */ 26 | @Command(name = "infozilla", version = "1.0") 27 | public class Main implements Runnable{ 28 | 29 | @Option(names = { "-s", "--with-stacktraces" }, description = "Process and extract stacktraces (default=true)") 30 | private boolean withStackTraces = true; 31 | 32 | @Option(names = { "-p", "--with-patches" }, description = "Process and extract patches (default=true)") 33 | private boolean withPatches = true; 34 | 35 | @Option(names = { "-c", "--with-source-code" }, description = "Process and extract source code regions (default=true)") 36 | private boolean withCode = true; 37 | 38 | @Option(names = { "-l", "--with-lists" }, description = "Process and extract lists (default=true)") 39 | private boolean withLists = true; 40 | 41 | @Option(names = "--charset", description = "Character Set of Input (default=ISO-8859-1)") 42 | private String inputCharset = "ISO-8859-1"; 43 | 44 | @Parameters(arity = "1..*", paramLabel = "FILE", description = "File(s) to process.") 45 | private File[] inputFiles; 46 | 47 | 48 | @Override 49 | public void run() { 50 | 51 | for (File f : inputFiles) { 52 | try { 53 | process(f); 54 | } catch (Exception e) { 55 | e.printStackTrace(); 56 | System.exit(1); 57 | } 58 | } 59 | 60 | } 61 | 62 | private void process(File f) throws Exception { 63 | // Read file 64 | String data = Files.readString(f.toPath(), Charset.forName(inputCharset)); 65 | 66 | // Run infozilla 67 | FilterChainEclipse infozilla_filters = new FilterChainEclipse(data, withPatches, withStackTraces, withCode, withLists); 68 | 69 | // Infozilla remembers the original input text 70 | // String original_text = infozilla_filters.getInputText(); 71 | 72 | // Infozilla can filter out all structural elements and output 73 | // the remaining (natural language text parts) of the input. 74 | String filtered_text = infozilla_filters.getOutputText(); 75 | 76 | System.out.println("Extracted Structural Elements from " + f.getAbsolutePath()); 77 | System.out.println(infozilla_filters.getPatches().size() + "\t Patches"); 78 | System.out.println(infozilla_filters.getTraces().size() + "\t Stack Traces"); 79 | System.out.println(infozilla_filters.getRegions().size() + "\t Source Code Fragments"); 80 | System.out.println(infozilla_filters.getEnumerations().size() + "\t Enumerations"); 81 | 82 | System.out.println("Writing Cleaned Output"); 83 | Files.writeString(Path.of(f.getAbsolutePath() + ".cleaned"), filtered_text, Charset.forName(inputCharset), StandardOpenOption.CREATE ); 84 | 85 | Element rootE = new Element("infozilla-output"); 86 | rootE.addContent(DataExportUtility.getXMLExportOfPatches(infozilla_filters.getPatches(), true)); 87 | rootE.addContent(DataExportUtility.getXMLExportOfStackTraces(infozilla_filters.getTraces(), true, new Timestamp(new Date().getTime()))); 88 | rootE.addContent(DataExportUtility.getXMLExportOfSourceCode(infozilla_filters.getRegions(), true)); 89 | rootE.addContent(DataExportUtility.getXMLExportOfEnumerations(infozilla_filters.getEnumerations(), true)); 90 | 91 | Document doc = new Document(rootE); 92 | XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat()); 93 | String xmlDoc = outputter.outputString(doc); 94 | 95 | System.out.println("Writing XML Output"); 96 | Files.writeString(Path.of(f.getAbsolutePath() + ".result.xml"), xmlDoc, Charset.forName(inputCharset), StandardOpenOption.CREATE ); 97 | } 98 | 99 | public static void main(String[] args) { 100 | CommandLine.run(new Main(), args); 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/FilterChainEclipse.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FilterChainEclipse.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import io.kuy.infozilla.elements.enumeration.Enumeration; 11 | import io.kuy.infozilla.elements.patch.Patch; 12 | import io.kuy.infozilla.elements.sourcecode.java.CodeRegion; 13 | import io.kuy.infozilla.elements.stacktrace.java.StackTrace; 14 | import io.kuy.infozilla.helpers.RegExHelper; 15 | 16 | /** 17 | * Class for runnning the complete filter chain on an eclipse input and gathering the results 18 | * @author Nicolas Bettenburg 19 | * 20 | */ 21 | public class FilterChainEclipse implements FilterChain { 22 | // Private Attributes 23 | private FilterPatches patchFilter; 24 | private FilterStackTraceJAVA stacktraceFilter; 25 | private FilterSourceCodeJAVA sourcecodeFilter; 26 | private FilterEnumeration enumFilter; 27 | 28 | private String inputText = ""; 29 | private String outputText = ""; 30 | 31 | private List patches; 32 | private List traces; 33 | private List regions; 34 | private List enumerations; 35 | 36 | // Constructor runs the experiments 37 | public FilterChainEclipse(String inputText) { 38 | patchFilter = new FilterPatches(); 39 | stacktraceFilter = new FilterStackTraceJAVA(); 40 | sourcecodeFilter = new FilterSourceCodeJAVA(FilterChainEclipse.class.getResource("/Java_CodeDB.txt")); 41 | enumFilter = new FilterEnumeration(); 42 | 43 | this.inputText = RegExHelper.makeLinuxNewlines(inputText); 44 | this.outputText = this.inputText; 45 | 46 | patches = patchFilter.runFilter(outputText); 47 | outputText = patchFilter.getOutputText(); 48 | 49 | traces = stacktraceFilter.runFilter(outputText); 50 | outputText = stacktraceFilter.getOutputText(); 51 | 52 | regions = sourcecodeFilter.runFilter(outputText); 53 | outputText = sourcecodeFilter.getOutputText(); 54 | 55 | enumerations = enumFilter.runFilter(outputText); 56 | // The output of the filter chain 57 | outputText = sourcecodeFilter.getOutputText(); 58 | } 59 | 60 | public FilterChainEclipse(String inputText, boolean runPatches, boolean runTraces, boolean runSource, boolean runEnums) { 61 | patchFilter = new FilterPatches(); 62 | stacktraceFilter = new FilterStackTraceJAVA(); 63 | sourcecodeFilter = new FilterSourceCodeJAVA(FilterChainEclipse.class.getResource("/Java_CodeDB.txt")); 64 | enumFilter = new FilterEnumeration(); 65 | 66 | 67 | this.inputText = RegExHelper.makeLinuxNewlines(inputText); 68 | this.outputText = this.inputText; 69 | 70 | if (runPatches) { 71 | patches = patchFilter.runFilter(outputText); 72 | } else patches = new ArrayList(); 73 | 74 | outputText = patchFilter.getOutputText(); 75 | 76 | if (runTraces) { 77 | traces = stacktraceFilter.runFilter(outputText); 78 | } else traces = new ArrayList(); 79 | 80 | outputText = stacktraceFilter.getOutputText(); 81 | 82 | if (runSource) { 83 | regions = sourcecodeFilter.runFilter(outputText); 84 | } else regions = new ArrayList(); 85 | 86 | outputText = sourcecodeFilter.getOutputText(); 87 | 88 | if (runEnums) { 89 | enumerations = enumFilter.runFilter(outputText); 90 | } else enumerations = new ArrayList(); 91 | 92 | // The output of the filter chain 93 | outputText = sourcecodeFilter.getOutputText(); 94 | } 95 | 96 | /** 97 | * @return the outputText 98 | */ 99 | public String getOutputText() { 100 | return outputText; 101 | } 102 | 103 | /** 104 | * @return the patches 105 | */ 106 | public List getPatches() { 107 | return patches; 108 | } 109 | 110 | /** 111 | * @return the traces 112 | */ 113 | public List getTraces() { 114 | return traces; 115 | } 116 | 117 | /** 118 | * @return the regions 119 | */ 120 | public List getRegions() { 121 | return regions; 122 | } 123 | 124 | /** 125 | * @return the enumerations 126 | */ 127 | public List getEnumerations() { 128 | return enumerations; 129 | } 130 | 131 | /** 132 | * @param inputText the inputText to set 133 | */ 134 | public void setInputText(String inputText) { 135 | this.inputText = inputText; 136 | } 137 | 138 | /** 139 | * @return the inputText 140 | */ 141 | public String getInputText() { 142 | return inputText; 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/FilterChainMozilla.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FilterChainMozilla.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import io.kuy.infozilla.elements.enumeration.Enumeration; 11 | import io.kuy.infozilla.elements.patch.Patch; 12 | import io.kuy.infozilla.elements.sourcecode.java.CodeRegion; 13 | import io.kuy.infozilla.elements.stacktrace.talkback.TalkbackTrace; 14 | import io.kuy.infozilla.helpers.RegExHelper; 15 | 16 | 17 | /** 18 | * Class for runnning the complete filter chain on a Mozilla input and gathering the results 19 | * @author Nicolas Bettenburg 20 | * 21 | */ 22 | public class FilterChainMozilla implements FilterChain { 23 | // Private Attributes 24 | private FilterPatches patchFilter; 25 | private FilterTalkBack stacktraceFilter; 26 | private FilterSourceCodeJAVA sourcecodeFilter; 27 | private FilterEnumeration enumFilter; 28 | 29 | private String inputText = ""; 30 | private String outputText = ""; 31 | 32 | private List patches; 33 | private List traces; 34 | private List regions; 35 | private List enumerations; 36 | 37 | // Constructor runs the experiments 38 | public FilterChainMozilla(String inputText) { 39 | patchFilter = new FilterPatches(); 40 | patchFilter.setRelaxed(true); 41 | 42 | stacktraceFilter = new FilterTalkBack(); 43 | sourcecodeFilter = new FilterSourceCodeJAVA(FilterChainMozilla.class.getResource("/Java_CodeDB.txt")); 44 | enumFilter = new FilterEnumeration(); 45 | 46 | this.inputText = RegExHelper.makeLinuxNewlines(inputText); 47 | this.outputText = this.inputText; 48 | 49 | patches = patchFilter.runFilter(outputText); 50 | outputText = patchFilter.getOutputText(); 51 | 52 | traces = stacktraceFilter.runFilter(outputText); 53 | outputText = stacktraceFilter.getOutputText(); 54 | 55 | regions = sourcecodeFilter.runFilter(outputText); 56 | outputText = sourcecodeFilter.getOutputText(); 57 | 58 | enumerations = enumFilter.runFilter(outputText); 59 | // The output of the filter chain 60 | outputText = sourcecodeFilter.getOutputText(); 61 | } 62 | 63 | public FilterChainMozilla(String inputText, boolean runPatches, boolean runTraces, boolean runSource, boolean runEnums) { 64 | patchFilter = new FilterPatches(); 65 | //patchFilter.setRelaxed(true); 66 | stacktraceFilter = new FilterTalkBack(); 67 | sourcecodeFilter = new FilterSourceCodeJAVA(FilterChainMozilla.class.getResource("/Java_CodeDB.txt")); 68 | enumFilter = new FilterEnumeration(); 69 | 70 | this.inputText = RegExHelper.makeLinuxNewlines(inputText); 71 | this.outputText = this.inputText; 72 | 73 | if (runPatches) patches = patchFilter.runFilter(outputText); 74 | else patches = new ArrayList(); 75 | 76 | outputText = patchFilter.getOutputText(); 77 | 78 | if (runTraces) traces = stacktraceFilter.runFilter(outputText); 79 | else traces = new ArrayList(); 80 | 81 | outputText = stacktraceFilter.getOutputText(); 82 | 83 | if (runSource) regions = sourcecodeFilter.runFilter(outputText); 84 | else regions = new ArrayList(); 85 | 86 | outputText = sourcecodeFilter.getOutputText(); 87 | 88 | if (runEnums) enumerations = enumFilter.runFilter(outputText); 89 | else enumerations = new ArrayList(); 90 | 91 | // The output of the filter chain 92 | outputText = sourcecodeFilter.getOutputText(); 93 | } 94 | 95 | /** 96 | * @return the outputText 97 | */ 98 | public String getOutputText() { 99 | return outputText; 100 | } 101 | 102 | /** 103 | * @return the patches 104 | */ 105 | public List getPatches() { 106 | return patches; 107 | } 108 | 109 | /** 110 | * @return the traces 111 | */ 112 | public List getTraces() { 113 | return traces; 114 | } 115 | 116 | /** 117 | * @return the regions 118 | */ 119 | public List getRegions() { 120 | return regions; 121 | } 122 | 123 | /** 124 | * @return the enumerations 125 | */ 126 | public List getEnumerations() { 127 | return enumerations; 128 | } 129 | 130 | /** 131 | * @param inputText the inputText to set 132 | */ 133 | public void setInputText(String inputText) { 134 | this.inputText = inputText; 135 | } 136 | 137 | /** 138 | * @return the inputText 139 | */ 140 | public String getInputText() { 141 | return inputText; 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/bugreports/MIME/MainMimeTyper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MainMimeTyper.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.bugreports.MIME; 6 | 7 | import java.util.Map; 8 | 9 | import io.kuy.infozilla.bugreports.BugReport; 10 | import io.kuy.infozilla.datasources.bugzilladb.DatabaseConnector; 11 | 12 | public class MainMimeTyper { 13 | 14 | /** 15 | * @param args 16 | * args[0] database host 17 | * args[1] database user 18 | * args[2] database user password 19 | * args[3] database name 20 | * args[4] bug report id to start with 21 | * args[5] amount of bug reports to process 22 | */ 23 | public static void main(String[] args) { 24 | 25 | if (args.length != 6) { 26 | usage(); 27 | System.exit(-1); 28 | } 29 | 30 | // Setup connection to bug report database 31 | DatabaseConnector dbc = new DatabaseConnector(args[0], args[1], args[2], args[3]); 32 | dbc.initialize(); 33 | if (!dbc.connect()) { 34 | System.out.println("Could not connect to database. Check host, port, username and password."); 35 | System.exit(1); 36 | } 37 | int startID = Integer.valueOf(args[4]); 38 | do { 39 | System.out.println("-------------------------------------------------------------------------------"); 40 | // Fetch a number of Bug Report unique IDs belonging to the following query 41 | 42 | String setSelectionQuery = "SELECT DISTINCT bug_id FROM bugzilla_bugs WHERE bug_id > " + startID + " ORDER BY bug_id ASC LIMIT " + args[5]; 43 | int[] bugIDs = dbc.getIdSetByQuery(setSelectionQuery); 44 | 45 | if (bugIDs.length == 0) 46 | break; 47 | 48 | int lastID = bugIDs[bugIDs.length -1]; 49 | 50 | System.out.println("-- Attempting to fetch " + bugIDs.length + " reports starting with ID " + startID); 51 | System.out.println("-------------------------------------------------------------------------------"); 52 | 53 | // Collect the bug reports from the database using up to 100 parallel threads. 54 | Map bugReports = dbc.getReportsConcurrent(bugIDs, true, false, false); 55 | System.out.println("-- Fetched " + bugReports.size() + " Reports"); 56 | 57 | if (Integer.valueOf(startID) == 0) { 58 | System.out.println("-------------------------------------------------------------------------------"); 59 | System.out.println("-- Empty metrics info, then write the table"); 60 | System.out.println("DELETE FROM bugzilla_attachments_mime"); 61 | System.out.println("-------------------------------------------------------------------------------"); 62 | } 63 | 64 | for (int id : bugIDs) { 65 | Map mimeTypesOfAttachaments = MimeTyper.discoverMimeTypesOfBugReport(id, dbc); 66 | for (String attach_id : mimeTypesOfAttachaments.keySet()) { 67 | String mimeString = mimeTypesOfAttachaments.get(attach_id); 68 | String[] parts = mimeString.split("; "); 69 | String mimeType = parts[0]; 70 | 71 | String encoding = "unknown"; 72 | if (parts.length > 1) { 73 | if (parts[1].length() > 8) { 74 | encoding = parts[1].substring(8, parts[1].length()); 75 | } 76 | } 77 | 78 | System.out.println("INSERT INTO bugzilla_attachments_mime (attach_id, mimetype, encoding, raw) VALUES (" 79 | + attach_id + ", " 80 | + "'" + escape(mimeType.trim()) + "', " 81 | + "'" + escape(encoding.trim()) + "', " 82 | + "'" + escape(mimeString.trim()) + "');"); 83 | } 84 | } 85 | 86 | // Output last processed id for the user to he can pick up execution there 87 | System.out.println("-------------------------------------------------------------------------------"); 88 | System.out.println("-- Last ID processed was " + lastID); 89 | System.out.println("-------------------------------------------------------------------------------"); 90 | startID = lastID; 91 | } while (true); 92 | 93 | // Disconnect the DatabaseConnector 94 | dbc.disconnect(); 95 | 96 | } 97 | 98 | private static void usage() { 99 | System.out.println("Usage: java -Xmx2000M -jar mimetyper.jar"); 100 | System.out.println("* args[0] database host"); 101 | System.out.println("* args[1] database user"); 102 | System.out.println("* args[2] database user password"); 103 | System.out.println("* args[3] database name"); 104 | System.out.println("* args[4] bug report id to start with"); 105 | System.out.println("* args[5] amount of bug reports to process"); 106 | } 107 | 108 | public static String escape(String input) { 109 | // Escape all \ with \\ 110 | String escaped = input.replaceAll("[\\\\]", "\\\\\\\\"); 111 | // All ' become '' 112 | escaped = escaped.replaceAll("'", "''"); 113 | return escaped; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/bugreports/BugReport.java: -------------------------------------------------------------------------------- 1 | /** 2 | * BugReport.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.bugreports; 6 | 7 | import java.sql.Timestamp; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * This class models a Bug Report in the BugZilla database systems. 13 | * @author Nicolas Bettenburg 14 | * 15 | */ 16 | public class BugReport implements Comparable { 17 | 18 | private int bug_id = -1; 19 | private String product_id = "unknown"; 20 | private String component_id = "unknown"; 21 | private String priority = "unknown"; 22 | private String assigned_to = "unknown"; 23 | private String bug_severity = "unknown"; 24 | private String bug_status = "unknown"; 25 | private Timestamp creation_ts; 26 | private String short_desc = "unknown"; 27 | private String resolution = "unknown"; 28 | private Discussion discussion; // have a priority queue of discussion articles ordered by Timestamp 29 | private List attachments; // have a list of attachements 30 | 31 | /** 32 | * Standard Constructor for an instance of BugReport 33 | * @param bug_id An integer value describing the unique ID of this Bug Report 34 | * @param assigned_to The developer this Bug Report was assigned to 35 | * @param bug_severity The severity of the Bug 36 | * @param bug_status The status of the Bug (like OPEN, CLOSED, RESOLVED, ...) 37 | * @param creation_ts The timestamp of when the Bug Report was created 38 | * @param short_desc A short summary of the Bug described (heading) 39 | * @param resolution The resolution of the Bug (like WONTFIX, WORKSFORME, ...) 40 | * @param discussion The complete discussion of the Report. The first discussion message is the description. 41 | */ 42 | public BugReport(int bug_id, 43 | String product_id, String component_id, String priority, 44 | String assigned_to, String bug_severity, 45 | String bug_status, Timestamp creation_ts, String short_desc, 46 | String resolution, Discussion discussion) { 47 | super(); 48 | this.bug_id = bug_id; 49 | this.product_id = product_id; 50 | this.component_id = component_id; 51 | this.priority = priority; 52 | this.assigned_to = assigned_to; 53 | this.bug_severity = bug_severity; 54 | this.bug_status = bug_status; 55 | this.creation_ts = creation_ts; 56 | this.short_desc = short_desc; 57 | this.resolution = resolution; 58 | 59 | // Instanciate the Discussion Class 60 | this.discussion = discussion; 61 | 62 | // Instanciate an empty attachment list 63 | this.attachments = new ArrayList(); 64 | } 65 | 66 | public int getBug_id() { 67 | return bug_id; 68 | } 69 | 70 | public String getAssigned_to() { 71 | return assigned_to; 72 | } 73 | 74 | public String getBug_severity() { 75 | return bug_severity; 76 | } 77 | 78 | public String getBug_status() { 79 | return bug_status; 80 | } 81 | 82 | public Timestamp getCreation_ts() { 83 | return creation_ts; 84 | } 85 | 86 | public String getShort_desc() { 87 | return short_desc; 88 | } 89 | 90 | public String getResolution() { 91 | if (resolution == null) 92 | return "NO RESOLUTION"; 93 | else 94 | return resolution; 95 | } 96 | 97 | public Discussion getDiscussion() { 98 | return discussion; 99 | } 100 | 101 | public String getDescription() { 102 | if (discussion.getMessages().size() > 0) 103 | return discussion.getDescription().getText(); 104 | else 105 | return ""; 106 | } 107 | 108 | public String getDiscussionText() { 109 | String discussionText = ""; 110 | for (Message m : discussion.getMessages()) { 111 | discussionText = discussionText + m.getText() + System.getProperty("line.separator") + System.getProperty("line.separator"); 112 | } 113 | return discussionText; 114 | } 115 | 116 | /** 117 | * The complete discussion until a specified date - useful when doing analysis for prior releases 118 | * @param untilDate 119 | * @return 120 | */ 121 | public String getDiscussionText(Timestamp untilDate) { 122 | String discussionText = ""; 123 | for (Message m : discussion.getMessages()) { 124 | if (m.getTime().before(untilDate)) 125 | discussionText = discussionText + m.getText() + System.getProperty("line.separator") + System.getProperty("line.separator"); 126 | } 127 | return discussionText; 128 | } 129 | 130 | public String getInitialMessageText() { 131 | String messageText = ""; 132 | if (discussion.getMessages().size() > 0) 133 | messageText = discussion.getMessages().get(0).getText(); 134 | 135 | return messageText; 136 | } 137 | 138 | public List getAttachments() { 139 | return attachments; 140 | } 141 | 142 | public int compareTo(BugReport that) { 143 | if (this.bug_id < that.bug_id) 144 | return -1; 145 | else if (this.bug_id > that.bug_id) 146 | return 1; 147 | else 148 | return 0; 149 | } 150 | 151 | public String getProduct_id() { 152 | return product_id; 153 | } 154 | 155 | public String getComponent_id() { 156 | return component_id; 157 | } 158 | 159 | public String getPriority() { 160 | return priority; 161 | } 162 | 163 | /** 164 | * @param attachments the attachments to set 165 | */ 166 | public void setAttachments(List attachments) { 167 | this.attachments = attachments; 168 | } 169 | 170 | 171 | } 172 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/FilterTalkBack.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FilterTalkBack.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.regex.MatchResult; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | import io.kuy.infozilla.elements.stacktrace.talkback.TalkbackEntry; 14 | import io.kuy.infozilla.elements.stacktrace.talkback.TalkbackTrace; 15 | import io.kuy.infozilla.helpers.RegExHelper; 16 | 17 | public class FilterTalkBack implements IFilter { 18 | 19 | private FilterTextRemover textRemover; 20 | 21 | public String getOutputText() { 22 | return textRemover.doDelete(); 23 | } 24 | 25 | public List runFilter(String inputText) { 26 | textRemover = new FilterTextRemover(inputText); 27 | 28 | List foundTraces = new ArrayList(); 29 | List talkbackLines = new ArrayList(); 30 | 31 | /* This regular expression can be used to filter TalkBack expressions 32 | * ( 33 | ([ \\n\\r]*(?:.*)(?:::)(?:.*)[ \\n\\r]*\\[.*?,?[ \\n\\r]*line[ \\n\\r]*[0-9]+\\]) 34 | (?:[ \\n\\r]*.*[ \\n\\r]*\\[.*?,?[ \\n\\r]*line[ \\n\\r]*[0-9]+\\]) 35 | (?:[ \\n\\r]*.*?\\(\\)) 36 | (?:[ \\n\\r]*.*?\\+[ \\n\\r]*[0-9]x[0-9a-zA-Z]+[ \\n\\r]*\\([0-9]x[0-9a-zA-Z]+\\)) 37 | (?:[ \\n\\r]*[0-9]x[0-9a-zA-Z]+) 38 | ){2,} 39 | */ 40 | 41 | String classmethodline = "([ \\n\\r]*(?:.*)(?:::)(?:.*)[ \\n\\r]*\\[.*?,?[ \\n\\r]*line[ \\n\\r]*[0-9]+\\])"; 42 | String methodline = "(?:[ \\n\\r]*.*[ \\n\\r]*\\[.*?,?[ \\n\\r]*line[ \\n\\r]*[0-9]+\\])"; 43 | String methodcallline = "([ \\n\\r]*[^ ]*?\\(\\)[ ]*[\\n\\r])"; 44 | String libraryline = "(?:[ \\n\\r]*.*?\\+[ \\n\\r]*[0-9]x[0-9a-zA-Z]+[ \\n\\r]*\\([0-9]x[0-9a-zA-Z]+\\))"; 45 | String addressline = "(?:[ \\n\\r]*[0-9]x[0-9a-zA-Z]+)"; 46 | 47 | String trace = "^(" + classmethodline + "|" + methodcallline + "|" + methodline + "|" + libraryline + "|" + addressline + "){2,}"; 48 | 49 | // Compile the patterns for reuse 50 | Pattern p_cml = Pattern.compile("^(" + classmethodline + ")", Pattern.MULTILINE); 51 | Pattern p_mcl = Pattern.compile("^(" + methodcallline + ")", Pattern.MULTILINE); 52 | Pattern p_ml = Pattern.compile("^(" + methodline + ")", Pattern.MULTILINE); 53 | Pattern p_ll = Pattern.compile("^(" + libraryline + ")", Pattern.MULTILINE); 54 | Pattern p_al = Pattern.compile("^(" + addressline + ")", Pattern.MULTILINE); 55 | Pattern ptl1 = Pattern.compile(trace, Pattern.MULTILINE); 56 | 57 | // Find all talkback lines 58 | for (MatchResult r : RegExHelper.findMatches(ptl1, inputText)) { 59 | talkbackLines.add(r.group().trim()); 60 | textRemover.markForDeletion(r.start(), r.end()); 61 | } 62 | 63 | // From each set of talkback lines create a talkback trace 64 | for (String line : talkbackLines){ 65 | String tmp = line; 66 | boolean hasMore = true; 67 | Matcher m = null; 68 | 69 | List entries = new ArrayList(); 70 | while (hasMore) { 71 | // We assume there are no more talkback lines. If there are matches this will be set to true. 72 | hasMore = false; 73 | 74 | // Check line for class method line 75 | m = p_cml.matcher(tmp); 76 | if (m.find()) 77 | if (m.start() == 0) { 78 | // Format the line 79 | String tbline = m.group().replaceAll("[\\n\\r]", "").trim(); 80 | // Split into name and location 81 | String[] info = tbline.split("\\["); 82 | TalkbackEntry anEntry = new TalkbackEntry(info[0],info[1].replaceAll("\\]",""),TalkbackEntry.CLASSMETHODLINE); 83 | 84 | // Add the entry to the talkback entries list 85 | entries.add(anEntry); 86 | 87 | // Remove this talback entry line from the string that is to be processed 88 | tmp = tmp.substring(m.end()); 89 | 90 | // Continue looking for further lines 91 | hasMore = true; 92 | continue; 93 | } 94 | // Check line for address line 95 | m = p_al.matcher(tmp); 96 | if (m.find()) 97 | if (m.start() == 0) { 98 | // Format the line 99 | String tbline = m.group().replaceAll("[\\n\\r]", "").trim(); 100 | 101 | TalkbackEntry anEntry = new TalkbackEntry(tbline, tbline, TalkbackEntry.ADDRESSLINE); 102 | 103 | // Add the entry to the talkback entries list 104 | entries.add(anEntry); 105 | 106 | // Remove this talback entry line from the string that is to be processed 107 | tmp = tmp.substring(m.end()); 108 | 109 | // Continue looking for further lines 110 | hasMore = true; 111 | continue; 112 | } 113 | // Check line for method call line 114 | m = p_mcl.matcher(tmp); 115 | if (m.find()) 116 | if (m.start() == 0) { 117 | // Format the line 118 | String tbline = m.group().replaceAll("[\\n\\r]", "").trim(); 119 | 120 | TalkbackEntry anEntry = new TalkbackEntry(tbline,"",TalkbackEntry.METHODCALLLINE); 121 | 122 | // Add the entry to the talkback entries list 123 | entries.add(anEntry); 124 | 125 | // Remove this talback entry line from the string that is to be processed 126 | tmp = tmp.substring(m.end()); 127 | 128 | // Continue looking for further lines 129 | hasMore = true; 130 | continue; 131 | } 132 | // Check line for method line 133 | m = p_ml.matcher(tmp); 134 | if (m.find()) 135 | if (m.start() == 0) { 136 | // Format the line 137 | String tbline = m.group().replaceAll("[\\n\\r]", "").trim(); 138 | 139 | // Split into name and location 140 | String[] info = tbline.split("\\["); 141 | TalkbackEntry anEntry = new TalkbackEntry(info[0],info[1].replaceAll("\\]",""),TalkbackEntry.METHODLINE); 142 | 143 | // Add the entry to the talkback entries list 144 | entries.add(anEntry); 145 | 146 | // Remove this talback entry line from the string that is to be processed 147 | tmp = tmp.substring(m.end()); 148 | 149 | // Continue looking for further lines 150 | hasMore = true; 151 | continue; 152 | } 153 | // Check line for library line 154 | m = p_ll.matcher(tmp); 155 | if (m.find()) 156 | if (m.start() == 0) { 157 | // Format the line 158 | String tbline = m.group().replaceAll("[\\n\\r]", "").trim(); 159 | 160 | // Split into name and location 161 | String[] info = tbline.split("\\("); 162 | TalkbackEntry anEntry = new TalkbackEntry(info[0],info[1].replaceAll("\\)",""),TalkbackEntry.LIBRARYLINE); 163 | 164 | // Add the entry to the talkback entries list 165 | entries.add(anEntry); 166 | 167 | // Remove this talback entry line from the string that is to be processed 168 | tmp = tmp.substring(m.end()); 169 | 170 | // Continue looking for further lines 171 | hasMore = true; 172 | continue; 173 | } 174 | } 175 | if (entries.size() > 0) { 176 | TalkbackTrace tbTrace = new TalkbackTrace(entries); 177 | foundTraces.add(tbTrace); 178 | } 179 | } 180 | 181 | return foundTraces; 182 | } 183 | 184 | } 185 | 186 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/bugreports/Attachment.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Attachment.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.bugreports; 6 | import java.io.UnsupportedEncodingException; 7 | import java.sql.Timestamp; 8 | import java.util.LinkedHashSet; 9 | import java.util.Set; 10 | 11 | public class Attachment { 12 | 13 | private String attachmentID; 14 | /** 15 | * @return the attachmentID 16 | */ 17 | public String getAttachmentID() { 18 | return attachmentID; 19 | } 20 | 21 | /** 22 | * @param attachmentID the attachmentID to set 23 | */ 24 | public void setAttachmentID(String attachmentID) { 25 | this.attachmentID = attachmentID; 26 | } 27 | 28 | private String description; 29 | private String mimetype; 30 | private String filename; 31 | private boolean isObsolete = false; 32 | private Timestamp creation_ts; 33 | private String type = null; 34 | private String magictype = null; 35 | private String encoding = null; 36 | private byte[] data; 37 | private int filesize = 0; 38 | 39 | /** 40 | * Mime-Types believed to contain text 41 | */ 42 | private static Set texttypes = new LinkedHashSet( 43 | java.util.Arrays.asList("application/text" ,"text/*" ,"text/bash-script" ,"text/css" ,"text/csv" ,"text/csv file" ,"text/diff" ,"text/html" ,"text/java" ,"text/java source" ,"text/js" ,"text/log" ,"text/plain" ,"text/plain, text/file" ,"text/x-csrc" ,"text/x-csv" ,"text/x-diff" ,"text/x-java" ,"text/x-java-source" ,"text/x-log" ,"text/xml" ,"text/xml " ,"text/x-patch") 44 | ); 45 | 46 | // Need Magic Types from UNIX file util since values returned by Database tend to be wrong! 47 | private static Set magictexttypes = new LinkedHashSet( 48 | java.util.Arrays.asList("text/html" ,"text/plain" ,"text/plain\0118bit" ,"text/troff" ,"text/x-asm" ,"text/x-c" ,"text/x-c++" ,"text/x-java" ,"text/x-mail" ,"text/x-makefile" ,"text/x-news") 49 | ); 50 | 51 | public static boolean isTextType(String mimetype, String magictype) { 52 | return (Attachment.texttypes.contains(mimetype) && Attachment.magictexttypes.contains(magictype)); 53 | } 54 | 55 | public boolean isText() { 56 | return (Attachment.texttypes.contains(mimetype) && Attachment.magictexttypes.contains(magictype)); 57 | //return magictype.contains("text"); 58 | } 59 | 60 | /** 61 | * Mime-Types believed to be binary/image 62 | */ 63 | private static Set imagetypes = new LinkedHashSet( 64 | java.util.Arrays.asList("application/gif" ,"application/jpg" ,"application/pdf" ,"application/png" ,"application/postscript" ,"application/x-shockwave-flash" ,"image/bar" ,"image/bitmap" ,"image/bmp" ,"image/gif" ,"image/ico" ,"image/icon" ,"image/jpeg" ,"image/jpeg " ,"image/pjpeg" ,"image/png" ,"image/svg+xml" ,"image/tiff" ,"image/x-bmp" ,"image/x-emf" ,"image/x-icon" ,"image/x-png" ,"image/x-portable-bitmap" ,"image/x-psd" ,"image/x-windows-bmp" ,"image/zip" ,"video/avi" ,"video/quicktime") 65 | ); 66 | 67 | private static Set magicimagetypes = new LinkedHashSet( 68 | java.util.Arrays.asList("application/x-shockwave-flash" ,"image/gif" ,"image/jpeg" ,"image/png" ,"image/tiff" ,"image/x-ms-bmp" ,"image/x-photoshop" ,"image/x-portable-bitmap" ,"image/x-portable-bitmap\0117bit" ,"image/x-xpmi" ,"video/quicktime" ,"video/unknown" ,"video/x-msvideo") 69 | ); 70 | 71 | public static boolean isImageType(String mimetype, String magictype) { 72 | return (Attachment.imagetypes.contains(mimetype) && Attachment.magicimagetypes.contains(magictype)); 73 | } 74 | 75 | public boolean isImage() { 76 | return (Attachment.imagetypes.contains(mimetype) && Attachment.magicimagetypes.contains(magictype)); 77 | //return magictype.contains("image"); 78 | } 79 | 80 | /** Overloaded Constructor 81 | * @param description The attachment's descriptive text 82 | * @param mimetype The mime-type of the attachment 83 | * @param filename The original filename 84 | * @param creation_ts The timestamp of the attachment's submission 85 | * @param type The attachments TYPE: SCREENSHOT, PATCH, STACKTRACE, SOURCECODE 86 | * @param data The binary Data of the attachment 87 | */ 88 | public Attachment(String description, String mimetype, String filename, 89 | Timestamp creation_ts, String type, byte[] data, int filesize) { 90 | super(); 91 | this.description = description; 92 | this.mimetype = mimetype; 93 | this.filename = filename; 94 | this.creation_ts = creation_ts; 95 | this.type = type; 96 | this.data = data; 97 | this.filesize = filesize; 98 | } 99 | /** 100 | * @return the description 101 | */ 102 | public String getDescription() { 103 | return description; 104 | } 105 | /** 106 | * @return the mimetype 107 | */ 108 | public String getMimetype() { 109 | return mimetype; 110 | } 111 | /** 112 | * @return the filename 113 | */ 114 | public String getFilename() { 115 | return filename; 116 | } 117 | /** 118 | * @return the creation_ts 119 | */ 120 | public Timestamp getCreation_ts() { 121 | return creation_ts; 122 | } 123 | /** 124 | * @return the type 125 | */ 126 | public String getType() { 127 | if (type ==null) 128 | guessType(); 129 | return type; 130 | } 131 | /** 132 | * @return the data 133 | */ 134 | public byte[] getData() { 135 | return data; 136 | } 137 | /** 138 | * @param type the type to set 139 | */ 140 | public void setType(String type) { 141 | this.type = type; 142 | } 143 | /** 144 | * @param data the data to set 145 | */ 146 | public void setData(byte[] data) { 147 | this.data = data; 148 | } 149 | 150 | /** 151 | * Try to guess the type of the attachment using some basic heuristics on mimetype and description 152 | * @return true if the type was guessed, false otherwise 153 | */ 154 | public boolean guessType() { 155 | boolean wasAbleToGuess = false; 156 | String guessedType = ""; 157 | 158 | // Rules for Screenshot 159 | if (mimetype.contains("image") 160 | || mimetype.contains("jpg") 161 | || mimetype.contains("gif") 162 | || mimetype.contains("jpeg") 163 | || mimetype.contains("bmp") 164 | || mimetype.contains("png") 165 | || mimetype.contains("video") 166 | || description.toLowerCase().contains("screenshot") 167 | || filename.toLowerCase().contains("screenshot")) 168 | guessedType = "SCREENSHOT"; 169 | 170 | // Rules for Patches 171 | if (mimetype.contains("patch") 172 | || mimetype.contains("diff") 173 | || description.toLowerCase().contains("patch") 174 | || description.toLowerCase().contains("diff") 175 | || filename.toLowerCase().contains("patch") 176 | || filename.toLowerCase().contains("diff")) 177 | guessedType = "PATCH"; 178 | 179 | // Rules for Source Code 180 | if (mimetype.contains("text/java") 181 | || mimetype.contains("text/java source") 182 | || mimetype.contains("text/x-java") 183 | || mimetype.contains("text/x-java source") 184 | || (description.toLowerCase().contains("source") && description.toLowerCase().contains("code")) 185 | || filename.toLowerCase().contains(".java")) 186 | guessedType = "SOURCECODE"; 187 | 188 | // Rules for Stack Traces 189 | if (mimetype.contains("text/log") 190 | || mimetype.contains("text/x-log") 191 | || (description.toLowerCase().contains("stack") && description.toLowerCase().contains("trace")) 192 | || filename.toLowerCase().contains("stacktrace")) 193 | guessedType = "STACKTRACE"; 194 | 195 | if (guessedType != "") { 196 | this.type = guessedType; 197 | wasAbleToGuess = true; 198 | } 199 | 200 | return wasAbleToGuess; 201 | } 202 | 203 | public String toString() { 204 | try { 205 | String output = new String(data, "UTF-8"); 206 | return output; 207 | } catch (UnsupportedEncodingException e) { 208 | return ""; 209 | } 210 | } 211 | 212 | /** 213 | * @return the filesize 214 | */ 215 | public int getFilesize() { 216 | return filesize; 217 | } 218 | 219 | /** 220 | * @return the magictype 221 | */ 222 | public String getMagictype() { 223 | return magictype; 224 | } 225 | 226 | /** 227 | * @return the encoding 228 | */ 229 | public String getEncoding() { 230 | return encoding; 231 | } 232 | 233 | /** 234 | * @param magictype the magictype to set 235 | */ 236 | public void setMagictype(String magictype) { 237 | this.magictype = magictype; 238 | } 239 | 240 | /** 241 | * @param encoding the encoding to set 242 | */ 243 | public void setEncoding(String encoding) { 244 | this.encoding = encoding; 245 | } 246 | 247 | public boolean isObsolete() { 248 | return isObsolete; 249 | } 250 | 251 | public void setObsolete(boolean isObsolete) { 252 | this.isObsolete = isObsolete; 253 | } 254 | 255 | } 256 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/FilterSourceCodeJAVA.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FilterSourceCodeJAVA.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.FileReader; 9 | import java.io.InputStream; 10 | import java.io.InputStreamReader; 11 | import java.net.URL; 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.regex.MatchResult; 16 | import java.util.regex.Pattern; 17 | 18 | import com.Ostermiller.util.CSVParser; 19 | 20 | import io.kuy.infozilla.elements.sourcecode.java.CodeRegion; 21 | import io.kuy.infozilla.helpers.RegExHelper; 22 | 23 | /** 24 | * The FilterSourceCodeJAVA class implements the IFilter interface for 25 | * JAVA source code structural elements. 26 | * @author Nicolas Bettenburg 27 | * 28 | */ 29 | public class FilterSourceCodeJAVA implements IFilter{ 30 | 31 | /** Stores the codePatterns read from Java_CodeDB.txt */ 32 | private HashMap codePatterns; 33 | 34 | /** Stores the code pattern options, read from Java_CodeDB.txt */ 35 | private HashMap codePatternOptions; 36 | 37 | /** The classes own textRemover */ 38 | private FilterTextRemover textRemover; 39 | 40 | /** 41 | * Standard Constructor 42 | */ 43 | public FilterSourceCodeJAVA() { 44 | codePatterns = new HashMap(); 45 | codePatternOptions = new HashMap(); 46 | } 47 | 48 | /** 49 | * Overloaded Constructor 50 | * @param filename the name of the file to read Code Patterns from. 51 | */ 52 | public FilterSourceCodeJAVA(String filename) { 53 | codePatterns = new HashMap(); 54 | codePatternOptions = new HashMap(); 55 | try { 56 | readCodePatterns(filename); 57 | } catch(Exception e) { 58 | System.err.println("Error while reading Java Source Code Patterns!"); 59 | e.printStackTrace(); 60 | } 61 | } 62 | 63 | /** 64 | * Overloaded Constructor 65 | * @param fileurl a URL to a file to read Code Patterns from. 66 | */ 67 | public FilterSourceCodeJAVA(URL fileurl) { 68 | codePatterns = new HashMap(); 69 | codePatternOptions = new HashMap(); 70 | try { 71 | readCodePatterns(fileurl.openStream()); 72 | } catch(Exception e) { 73 | System.err.println("Error while reading Java Source Code Patterns!"); 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | /** 79 | * Get a List of Source Code Regions contained in a given Text {@link s} 80 | * @param s the Text we shall look inside for Source Code 81 | * @return a List of Source Code Occurences as {@link CodeRegion}s 82 | */ 83 | private List getCodeRegions(final String s, boolean minimalSet) { 84 | List codeRegions = new ArrayList(); 85 | // for each keyword-pattern pair find the corresponding occurences! 86 | for (String keyword : codePatterns.keySet()) { 87 | Pattern p = codePatterns.get(keyword); 88 | String patternOptions = codePatternOptions.get(keyword); 89 | if (patternOptions.contains("MATCH")) { 90 | for (MatchResult r : RegExHelper.findMatches(p, s)) { 91 | int offset = findMatch(s,'{', '}', r.end()); 92 | CodeRegion foundRegion = new CodeRegion(r.start(),r.end() + offset, keyword, s.substring(r.start(), r.end() + offset)); 93 | codeRegions.add(foundRegion); 94 | } 95 | } 96 | else { 97 | for (MatchResult r : RegExHelper.findMatches(p, s)) { 98 | CodeRegion foundRegion = new CodeRegion(r.start(),r.end(), keyword, r.group()); 99 | codeRegions.add(foundRegion); 100 | } 101 | } 102 | 103 | } 104 | 105 | if (minimalSet) 106 | return makeMinimalSet(codeRegions); 107 | else 108 | return codeRegions; 109 | } 110 | 111 | 112 | /** 113 | * findMatch() returns the offset where the next closing is found. If not found return 0 114 | */ 115 | private int findMatch(String where, char opening, char closing, int start) { 116 | String region = where.substring(start); 117 | int level = 0; 118 | int position = 0; 119 | for (char c : region.toCharArray()) { 120 | position = position +1; 121 | if (c == opening) level=level+1; 122 | if (c == closing) { 123 | if (level == 0) { return position; } 124 | else {level = level -1;} 125 | } 126 | } 127 | return 0; 128 | } 129 | 130 | /** 131 | * Read in some Code Patterns from a file named {@link filename} 132 | * @param filename the full qualified filename from which to read the code patterns. 133 | * @throws Exception if there did something go wrong with I/O 134 | */ 135 | private void readCodePatterns(final String filename) throws Exception { 136 | BufferedReader fileInput = new BufferedReader(new FileReader(filename)); 137 | // Read patterns from the file 138 | String inputLine = null; 139 | while ( (inputLine = fileInput.readLine()) != null ) { 140 | // Input comes in the format: "keyword","PATTERN","OPTIONS" 141 | // A line can be commented out by using // 142 | if (!inputLine.substring(0, 2).equalsIgnoreCase("//")) { 143 | // we use ostermillers CSV Parser for sake of ease 144 | String[][] parsedLine = CSVParser.parse(inputLine); 145 | String keyword = parsedLine[0][0]; 146 | String pattern = parsedLine[0][1]; 147 | // Check if we have some options 148 | if (parsedLine[0].length == 3) { 149 | String options = parsedLine[0][2]; 150 | codePatternOptions.put(keyword, options); 151 | } else { 152 | codePatternOptions.put(keyword, ""); 153 | } 154 | Pattern somePattern = Pattern.compile(pattern); 155 | codePatterns.put(keyword, somePattern); 156 | } 157 | } 158 | fileInput.close(); 159 | } 160 | 161 | /** 162 | * Read in some Code Patterns from an input stream name {@link instream} 163 | * @param instream the input stream to read the code patterns from 164 | * @throws Exception if something goes wrong with I/O 165 | */ 166 | private void readCodePatterns(final InputStream instream) throws Exception { 167 | BufferedReader fileInput = new BufferedReader(new InputStreamReader(instream)); 168 | // Read patterns from the file 169 | String inputLine = null; 170 | while ( (inputLine = fileInput.readLine()) != null ) { 171 | // Input comes in the format: "keyword","PATTERN","OPTIONS" 172 | // A line can be commented out by using // 173 | if (!inputLine.substring(0, 2).equalsIgnoreCase("//")) { 174 | // we use Ostermillers CSV Parser for sake of ease 175 | String[][] parsedLine = CSVParser.parse(inputLine); 176 | String keyword = parsedLine[0][0]; 177 | String pattern = parsedLine[0][1]; 178 | // Check if we have some options 179 | if (parsedLine[0].length == 3) { 180 | String options = parsedLine[0][2]; 181 | codePatternOptions.put(keyword, options); 182 | } else { 183 | codePatternOptions.put(keyword, ""); 184 | } 185 | Pattern somePattern = Pattern.compile(pattern); 186 | codePatterns.put(keyword, somePattern); 187 | } 188 | } 189 | } 190 | 191 | 192 | /** 193 | * Given a List of Code Regions transform that list to a minimal including set 194 | * @param regionList a List of Code Regions that should be minimized 195 | * @return a minimal inclusion set of Code Regions. 196 | */ 197 | public static List makeMinimalSet(List regionList) { 198 | // Create a copy of the Code Region List 199 | List sortedRegionList = new ArrayList(regionList); 200 | // Sort it Ascending (by start position) 201 | java.util.Collections.sort(sortedRegionList); 202 | // This will hold the minimal set 203 | List minimalSet = new ArrayList(); 204 | 205 | // For each Element, see if it is contained in any previous element 206 | for (int i=0; i < sortedRegionList.size(); i++ ) { 207 | CodeRegion thisRegion = sortedRegionList.get(i); 208 | boolean contained = false; 209 | for (int j=0; j < i; j++) { 210 | CodeRegion thatRegion = sortedRegionList.get(j); 211 | if (thatRegion.end >= thisRegion.end) { 212 | contained = true; 213 | } 214 | } 215 | if (! contained) { 216 | minimalSet.add(thisRegion); 217 | } 218 | } 219 | return minimalSet; 220 | } 221 | 222 | public String getOutputText() { 223 | return textRemover.doDelete(); 224 | } 225 | 226 | public List runFilter(String inputText) { 227 | // Initialize a TextRemover 228 | textRemover = new FilterTextRemover(inputText); 229 | 230 | // Find all Code Regions in the given Text inputText - by default we want the minimal set 231 | // which means the outer most syntactical elements spanning all the discovered code. 232 | List codeRegions = getCodeRegions(inputText, true); 233 | 234 | // Mark the found Regions for deletion 235 | for (CodeRegion region : codeRegions) 236 | textRemover.markForDeletion(region.start, region.end); 237 | 238 | // Return the found source code regions 239 | return codeRegions; 240 | } 241 | 242 | } 243 | 244 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/RelaxedPatchParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RelaxedPatchParser.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import io.kuy.infozilla.elements.patch.Patch; 11 | import io.kuy.infozilla.elements.patch.PatchHunk; 12 | 13 | public class RelaxedPatchParser { 14 | // Uncomment the following line if you need debug output 15 | private final static boolean debug = false; 16 | 17 | /** 18 | * Find the next line among the set of lines[] that looks like a Patch Index: line. 19 | * @param lines the lines in which to look for the patch start. 20 | * @param start the starting line number from where to start the search. 21 | * @return the linenumber where we found the next patch start from {@link start}. 22 | */ 23 | private int findNextIndex(String[] lines, int start) { 24 | int found = -1; 25 | for (int i=start; i < lines.length-1; i++) { 26 | // Find the next line that starts with "+++ " 27 | if (lines[i].startsWith("--- ")) { 28 | // Check if the following line starts with "--- " 29 | if (lines[i+1].startsWith("+++ ")) { 30 | found = i; 31 | break; 32 | } 33 | 34 | } 35 | } 36 | return found; 37 | } 38 | 39 | /** 40 | * Find the next Hunk start beginning from {@link start} in a set of {@link lines}. 41 | * @param lines The lines to search for the next Hunk start. 42 | * @param start The line from which to start looking for the next Hunk. 43 | * @return The linenumber where the next Hunk start was found. 44 | */ 45 | private int findNextHunkHeader(String[] lines, int start) { 46 | int found = -1; 47 | for (int i=start; i < lines.length; i++) { 48 | // Find the next line that matches @@ -X,Y +XX,YY @@ 49 | if (lines[i].matches("^@@\\s\\-\\d+,\\d+\\s\\+\\d+,\\d+\\s@@.*?$")) { 50 | found = i; 51 | break; 52 | } 53 | } 54 | return found; 55 | } 56 | 57 | /** 58 | * Splits a text into a List of possible Patches. 59 | * @param text The text to split into patches. 60 | * @return a List of possible Patches. 61 | */ 62 | private List partitionByIndex(String text) { 63 | // This will be a list of all potential Patch Areas 64 | List indexPartition = new ArrayList(); 65 | 66 | // Split the complete text into single lines to work with 67 | String[] lines = text.split("[\n\r]"); 68 | 69 | // When we start we think there are more Patches inside ;) 70 | boolean hasMore = true; 71 | 72 | // We start at the very beginning of our text 73 | int idxStart = -1; 74 | 75 | // Find all areas 76 | while (hasMore) { 77 | idxStart = findNextIndex(lines, idxStart +1); 78 | if (idxStart == -1) { 79 | // if there is no next start we are done 80 | hasMore = false; 81 | } else { 82 | // otherwise see if there is another index 83 | int idxEnd = findNextIndex(lines, idxStart +1); 84 | if (idxEnd == -1) { 85 | // add the whole range because there is no more next idx start 86 | String range = ""; 87 | for (int i = idxStart; i < lines.length -1; i++) { 88 | range = range + lines[i] + System.getProperty("line.separator"); 89 | } 90 | range = range + lines[lines.length-1]; 91 | indexPartition.add(range); 92 | } else { 93 | // there is another index start so add the range to the partition 94 | String range = ""; 95 | for (int i = idxStart; i < idxEnd; i++) { 96 | range = range + lines[i] + System.getProperty("line.separator"); 97 | } 98 | indexPartition.add(range); 99 | 100 | // and set the new idxStart to end ! 101 | idxStart = idxEnd -1; 102 | } 103 | } 104 | } 105 | 106 | return indexPartition; 107 | } 108 | 109 | /** 110 | * Find the first line that starts with a given String 111 | * @param text The text the line we look for starts with 112 | * @param lines An Array of lines 113 | * @param start The line number to start the search with 114 | * @return The index of the first line starting at {@link start} or -1 if there is no such line 115 | */ 116 | private int findFirstLineBeginningWith(String text, String[] lines, int start) { 117 | int found = -1; 118 | for (int i=start; i < lines.length; i++) { 119 | if (lines[i].startsWith(text)) { 120 | found = i; 121 | break; 122 | } 123 | } 124 | return found; 125 | } 126 | 127 | 128 | /** 129 | * Find the first line that starts with a given String 130 | * @param text The text the line we look for starts with 131 | * @param lines An Array of lines 132 | * @param start The line number to start the search with 133 | * @return The first line starting at {@link start} or an empty String if there is no such line 134 | */ 135 | private String findFirstLineBeginningWithS(String text, String[] lines, int start) { 136 | String found = ""; 137 | for (int i=start; i < lines.length; i++) { 138 | if (lines[i].startsWith(text)) { 139 | found = lines[i]; 140 | break; 141 | } 142 | } 143 | return found; 144 | } 145 | 146 | 147 | /** 148 | * Checks whether the given line is a line that belongs to a hunk or not. 149 | * @param line the line to check for being a Hunk Line. 150 | * @return true if the {@link line} is a Hunk line, false otherwise. 151 | */ 152 | private boolean isHunkLine(String line) { 153 | boolean isHunkLine = ((line.startsWith("+")) || (line.startsWith("-")) || (line.startsWith(" "))); 154 | return isHunkLine; 155 | } 156 | 157 | /** 158 | * Find and extract all Hunks in a Patch 159 | * @param lines A set of Patch Lines 160 | * @param start The line to start looking for Hunks 161 | * @return a List of Hunks that were found 162 | */ 163 | private List findAllHunks(String[] lines, int start) { 164 | List foundHunks = new ArrayList(); 165 | String lineSep = System.getProperty("line.separator"); 166 | int hStart = start-1; 167 | boolean hasMore = true; 168 | while (hasMore) { 169 | hStart = findNextHunkHeader(lines, hStart+1); 170 | // Check if there are more Hunks 171 | if (hStart == -1) { 172 | // If there are no more Hunks then we are finished 173 | if (debug) System.out.println("<>>> No More Hunks found! Finished!"); 174 | hasMore = false; 175 | } else { 176 | // If there are then look for the next Hunk start 177 | if (debug) System.out.println("<>>> Hunk Start is " + hStart); 178 | int nextHunkStart = findNextHunkHeader(lines, hStart + 1); 179 | int searchEnd = 0; 180 | if (nextHunkStart == -1) { 181 | if (debug) System.out.println("<>>> There are no more Hunks!"); 182 | // If there is no next Hunk we can process until the end 183 | searchEnd = lines.length; 184 | hasMore = false; 185 | } else { 186 | if (debug) System.out.println("<>>> There are more Hunks left!"); 187 | // Otherwise we will look only until the next Hunk beginning 188 | searchEnd = nextHunkStart -1; 189 | } 190 | if (debug) System.out.println("<>>> Will look for HunkLines from " + (hStart+1) + " to " + (searchEnd-1)); 191 | String hunktext = ""; 192 | for (int i = hStart +1; i < searchEnd; i++) { 193 | if (debug) System.out.println("<>>> Checking if Hunkline: " + lines[i]); 194 | if (isHunkLine(lines[i])) { 195 | if (debug) System.out.println("<>>> Yes it is!"); 196 | hunktext = hunktext + lines[i] + lineSep; 197 | } else { 198 | if (i < searchEnd -1) { 199 | if (isHunkLine(lines[i+1])) { 200 | if (debug) System.out.println("<>>> No But next line is!"); 201 | hunktext = hunktext + lines[i] + lineSep; 202 | } 203 | else { 204 | // we are done 205 | if (debug) System.out.println("<>>> No it is not and niether is the next one! We should stop here!"); 206 | searchEnd = i; 207 | } 208 | } 209 | } 210 | } 211 | // Kill last newline 212 | if (hunktext.length() > 1) 213 | hunktext = hunktext.substring(0, hunktext.length()-1); 214 | foundHunks.add(new PatchHunk(hunktext)); 215 | hStart = nextHunkStart -1; 216 | } 217 | } 218 | return foundHunks; 219 | } 220 | 221 | /** 222 | * Parses a given text for all Patches inside using a 2 line lookahead Fuzzy Parser approach. 223 | * @param text The text to extract Patches from. 224 | * @return a list of {@link foundPatches}. 225 | */ 226 | public List parseForPatches(String text) { 227 | // Start with an empty list of Patches 228 | List foundPatches = new ArrayList(); 229 | 230 | // First Partition the whole given text into sections starting with Index: 231 | // The parts of the partition mark on potential patch 232 | List indexPartition = partitionByIndex(text); 233 | 234 | // For each potential patch area split into header and a list of potential hunks 235 | for (String potentialPatch : indexPartition) { 236 | String[] lines = potentialPatch.split("[\n\r]"); 237 | 238 | Patch patch = new Patch(); 239 | // Gather Header Information of the Patch 240 | String pIndex = findFirstLineBeginningWithS("Index: ", lines, 0); 241 | patch.setIndex(pIndex); 242 | String pOrig = findFirstLineBeginningWithS("--- ", lines, 0); 243 | patch.setOriginalFile(pOrig); 244 | String pModi = findFirstLineBeginningWithS("+++ ", lines, 0); 245 | patch.setModifiedFile(pModi); 246 | 247 | // Find the first Hunk Header 248 | int pModiNum = findFirstLineBeginningWith("+++ ", lines, 0); 249 | int firstHunkLine = findNextHunkHeader(lines, pModiNum + 1); 250 | 251 | // If there is no Hunk then the patch is invalid! 252 | if (firstHunkLine == -1) 253 | break; 254 | 255 | // Now we can add the complete Header 256 | String header = ""; 257 | for (int i=0; i < firstHunkLine-1; i++) { 258 | header = header + lines[i] + System.getProperty("line.separator"); 259 | } 260 | header = header + lines[firstHunkLine-1]; 261 | patch.setHeader(header); 262 | 263 | // Discover all Hunks! 264 | List hunks = findAllHunks(lines, firstHunkLine); 265 | 266 | // And add the Hunks to the List of Hunks for this patch 267 | for (PatchHunk h : hunks) patch.addHunk(h); 268 | foundPatches.add(patch); 269 | } 270 | 271 | // Locate the Patches in the Source Code 272 | for (Patch p : foundPatches) { 273 | int patchStart = text.indexOf(p.getHeader()); 274 | 275 | int patchEnd = text.lastIndexOf(p.getHunks().get(p.getHunks().size()-1).getText()) 276 | + p.getHunks().get(p.getHunks().size()-1).getText().length(); 277 | 278 | p.setStartPosition(patchStart); 279 | p.setEndPosition(patchEnd); 280 | } 281 | 282 | // Here is the patch we found 283 | return foundPatches; 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/PatchParser.java: -------------------------------------------------------------------------------- 1 | /** 2 | * PatchParser.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import io.kuy.infozilla.elements.patch.Patch; 11 | import io.kuy.infozilla.elements.patch.PatchHunk; 12 | 13 | public class PatchParser { 14 | // Change the following line if you need debug output 15 | // Debug output is VERY verbose, so it's turned off by default! 16 | private final static boolean debug = false; 17 | 18 | /** 19 | * Find the next line among the set of lines[] that looks like a Patch Index: line. 20 | * @param lines the lines in which to look for the patch start. 21 | * @param start the starting line number from where to start the search. 22 | * @return the linenumber where we found the next patch start from {@link start}. 23 | */ 24 | private int findNextIndex(String[] lines, int start) { 25 | int found = -1; 26 | for (int i=start; i < lines.length-1; i++) { 27 | // Find the next line that starts with "Index: " 28 | if (lines[i].startsWith("Index: ")) { 29 | // Check if the following line starts with "=====" 30 | if (lines[i+1].startsWith("====")) { 31 | found = i; 32 | break; 33 | } 34 | 35 | } 36 | } 37 | return found; 38 | } 39 | 40 | /** 41 | * Find the next Hunk start beginning from {@link start} in a set of {@link lines}. 42 | * @param lines The lines to search for the next Hunk start. 43 | * @param start The line from which to start looking for the next Hunk. 44 | * @return The linenumber where the next Hunk start was found. 45 | */ 46 | private int findNextHunkHeader(String[] lines, int start) { 47 | int found = -1; 48 | for (int i=start; i < lines.length; i++) { 49 | // Find the next line that matches @@ -X,Y +XX,YY @@ 50 | if (lines[i].matches("^@@\\s\\-\\d+,\\d+\\s\\+\\d+,\\d+\\s@@$")) { 51 | found = i; 52 | break; 53 | } 54 | } 55 | return found; 56 | } 57 | 58 | /** 59 | * Splits a text into a List of possible Patches. 60 | * @param text The text to split into patches. 61 | * @return a List of possible Patches. 62 | */ 63 | private List partitionByIndex(String text) { 64 | // This will be a list of all potential Patch Areas 65 | List indexPartition = new ArrayList(); 66 | 67 | // Split the complete text into single lines to work with 68 | String[] lines = text.split("[\n\r]"); 69 | 70 | // When we start we think there are more Patches inside ;) 71 | boolean hasMore = true; 72 | 73 | // We start at the very beginning of our text 74 | int idxStart = -1; 75 | 76 | // Find all areas 77 | while (hasMore) { 78 | idxStart = findNextIndex(lines, idxStart +1); 79 | if (idxStart == -1) { 80 | // if there is no next start we are done 81 | hasMore = false; 82 | } else { 83 | // otherwise see if there is another index 84 | int idxEnd = findNextIndex(lines, idxStart +1); 85 | if (idxEnd == -1) { 86 | // add the whole range because there is no more next idx start 87 | String range = ""; 88 | for (int i = idxStart; i < lines.length -1; i++) { 89 | range = range + lines[i] + System.getProperty("line.separator"); 90 | } 91 | range = range + lines[lines.length-1]; 92 | indexPartition.add(range); 93 | } else { 94 | // there is another index start so add the range to the partition 95 | String range = ""; 96 | for (int i = idxStart; i < idxEnd; i++) { 97 | range = range + lines[i] + System.getProperty("line.separator"); 98 | } 99 | indexPartition.add(range); 100 | 101 | // and set the new idxStart to end ! 102 | idxStart = idxEnd -1; 103 | } 104 | } 105 | } 106 | 107 | return indexPartition; 108 | } 109 | 110 | /** 111 | * Find the first line that starts with a given String 112 | * @param text The text the line we look for starts with 113 | * @param lines An Array of lines 114 | * @param start The line number to start the search with 115 | * @return The index of the first line starting at {@link start} or -1 if there is no such line 116 | */ 117 | private int findFirstLineBeginningWith(String text, String[] lines, int start) { 118 | int found = -1; 119 | for (int i=start; i < lines.length; i++) { 120 | if (lines[i].startsWith(text)) { 121 | found = i; 122 | break; 123 | } 124 | } 125 | return found; 126 | } 127 | 128 | 129 | /** 130 | * Find the first line that starts with a given String 131 | * @param text The text the line we look for starts with 132 | * @param lines An Array of lines 133 | * @param start The line number to start the search with 134 | * @return The first line starting at {@link start} or an empty String if there is no such line 135 | */ 136 | private String findFirstLineBeginningWithS(String text, String[] lines, int start) { 137 | String found = ""; 138 | for (int i=start; i < lines.length; i++) { 139 | if (lines[i].startsWith(text)) { 140 | found = lines[i]; 141 | break; 142 | } 143 | } 144 | return found; 145 | } 146 | 147 | 148 | /** 149 | * Checks whether the given line is a line that belongs to a hunk or not. 150 | * @param line the line to check for being a Hunk Line. 151 | * @return true if the {@link line} is a Hunk line, false otherwise. 152 | */ 153 | private boolean isHunkLine(String line) { 154 | boolean isHunkLine = ((line.startsWith("+")) || (line.startsWith("-")) || (line.startsWith(" "))); 155 | return isHunkLine; 156 | } 157 | 158 | /** 159 | * Find and extract all Hunks in a Patch 160 | * @param lines A set of Patch Lines 161 | * @param start The line to start looking for Hunks 162 | * @return a List of Hunks that were found 163 | */ 164 | private List findAllHunks(String[] lines, int start) { 165 | List foundHunks = new ArrayList(); 166 | String lineSep = System.getProperty("line.separator"); 167 | int hStart = start-1; 168 | boolean hasMore = true; 169 | while (hasMore) { 170 | hStart = findNextHunkHeader(lines, hStart+1); 171 | // Check if there are more Hunks 172 | if (hStart == -1) { 173 | // If there are no more Hunks then we are finished 174 | if (debug) System.out.println("<>>> No More Hunks found! Finished!"); 175 | hasMore = false; 176 | } else { 177 | // If there are then look for the next Hunk start 178 | if (debug) System.out.println("<>>> Hunk Start is " + hStart); 179 | int nextHunkStart = findNextHunkHeader(lines, hStart + 1); 180 | int searchEnd = 0; 181 | if (nextHunkStart == -1) { 182 | if (debug) System.out.println("<>>> There are no more Hunks!"); 183 | // If there is no next Hunk we can process until the end 184 | searchEnd = lines.length; 185 | hasMore = false; 186 | } else { 187 | if (debug) System.out.println("<>>> There are more Hunks left!"); 188 | // Otherwise we will look only until the next Hunk beginning 189 | searchEnd = nextHunkStart -1; 190 | } 191 | if (debug) System.out.println("<>>> Will look for HunkLines from " + (hStart+1) + " to " + (searchEnd-1)); 192 | String hunktext = ""; 193 | for (int i = hStart +1; i < searchEnd; i++) { 194 | if (debug) System.out.println("<>>> Checking if Hunkline: " + lines[i]); 195 | if (isHunkLine(lines[i])) { 196 | if (debug) System.out.println("<>>> Yes it is!"); 197 | hunktext = hunktext + lines[i] + lineSep; 198 | } else { 199 | if (i < searchEnd -1) { 200 | if (isHunkLine(lines[i+1])) { 201 | if (debug) System.out.println("<>>> No But next line is!"); 202 | hunktext = hunktext + lines[i] + lineSep; 203 | } 204 | else { 205 | // we are done 206 | if (debug) System.out.println("<>>> No it is not and niether is the next one! We should stop here!"); 207 | searchEnd = i; 208 | } 209 | } 210 | } 211 | } 212 | // Kill last newline 213 | if (hunktext.length() > 1) 214 | hunktext = hunktext.substring(0, hunktext.length()-1); 215 | foundHunks.add(new PatchHunk(hunktext)); 216 | hStart = nextHunkStart -1; 217 | } 218 | } 219 | return foundHunks; 220 | } 221 | 222 | /** 223 | * Parses a given text for all Patches inside using a 2 line lookahead Fuzzy Parser approach. 224 | * @param text The text to extract Patches from. 225 | * @return a list of {@link foundPatches}. 226 | */ 227 | public List parseForPatches(String text) { 228 | // Start with an empty list of Patches 229 | List foundPatches = new ArrayList(); 230 | 231 | // First Partition the whole given text into sections starting with Index: 232 | // The parts of the partition mark on potential patch 233 | List indexPartition = partitionByIndex(text); 234 | 235 | // For each potential patch area split into header and a list of potential hunks 236 | for (String potentialPatch : indexPartition) { 237 | String[] lines = potentialPatch.split("[\n\r]"); 238 | 239 | Patch patch = new Patch(); 240 | // Gather Header Information of the Patch 241 | String pIndex = findFirstLineBeginningWithS("Index: ", lines, 0); 242 | patch.setIndex(pIndex); 243 | String pOrig = findFirstLineBeginningWithS("--- ", lines, 0); 244 | patch.setOriginalFile(pOrig); 245 | String pModi = findFirstLineBeginningWithS("+++ ", lines, 0); 246 | patch.setModifiedFile(pModi); 247 | 248 | // Find the first Hunk Header 249 | int pModiNum = findFirstLineBeginningWith("+++ ", lines, 0); 250 | int firstHunkLine = findNextHunkHeader(lines, pModiNum + 1); 251 | 252 | // If there is no Hunk then the patch is invalid! 253 | if (firstHunkLine == -1) 254 | break; 255 | 256 | // Now we can add the complete Header 257 | String header = ""; 258 | for (int i=0; i < firstHunkLine-1; i++) { 259 | header = header + lines[i] + System.getProperty("line.separator"); 260 | } 261 | header = header + lines[firstHunkLine-1]; 262 | patch.setHeader(header); 263 | 264 | // Discover all Hunks! 265 | List hunks = findAllHunks(lines, firstHunkLine); 266 | 267 | // And add the Hunks to the List of Hunks for this patch 268 | for (PatchHunk h : hunks) patch.addHunk(h); 269 | foundPatches.add(patch); 270 | } 271 | 272 | // Locate the Patches in the Source Code 273 | for (Patch p : foundPatches) { 274 | int patchStart = text.indexOf(p.getHeader()); 275 | 276 | int patchEnd = text.lastIndexOf(p.getHunks().get(p.getHunks().size()-1).getText()) 277 | + p.getHunks().get(p.getHunks().size()-1).getText().length(); 278 | 279 | p.setStartPosition(patchStart); 280 | p.setEndPosition(patchEnd); 281 | } 282 | 283 | // Here is the patch we found 284 | return foundPatches; 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # infoZilla 2 | The `infoZilla` tool is a library and tool for extracting structural software engineering data from unstructrured data sources such as e-mails, discussions, bug reports and wiki pages. 3 | 4 | Out of the box, the `infoZilla` tool finds the following artefacts: 5 | 6 | ## Java Source Code Regions 7 | Source code as small- to medium-sized code examples are often used to illustrate a problem, describe the programming context in which a problem occurred, or represent a sample fix to the problem described in the text. 8 | 9 | ![source code](docs/annotated_source.png) 10 | 11 | ## Patches 12 | 13 | Patches represent a small piece of software designed to update or fix problems with a computer program or supporting data. The mostly used format of patches is the unified diff format. 14 | 15 | ![patches](docs/annotated_patch.png) 16 | 17 | ## Enumerations and Itemizations 18 | Enumerations and itemizations are used to list items, describe a chain of causality, or present a sequence of actions the developer has to take in order to be able to reproduce or fix a problem. They add structure to the description of a problem and ease understanding. 19 | 20 | ![enumerations](docs/annotated_enum.png) 21 | 22 | ## Java Stacktraces 23 | Stack traces list the active stack frames in the calling stack upon execution of a program. They are widely used to aid bug fixing by giving hints at the origin of a problem. 24 | 25 | ![stacktraces](docs/annotated_trace.png) 26 | 27 | ## Talkback Traces 28 | Talkback traces detail the crash contexts and environment details when a problem is detected. 29 | 30 | ![talkback](docs/annotated_talkback.png) 31 | 32 | ## Usage 33 | ``` 34 | Usage: infozilla [-clps] [--charset=] FILE... 35 | FILE... File(s) to process. 36 | --charset= 37 | Character Set of Input (default=ISO-8859-1) 38 | -c, --with-source-code Process and extract source code regions (default=true) 39 | -l, --with-lists Process and extract lists (default=true) 40 | -p, --with-patches Process and extract patches (default=true) 41 | -s, --with-stacktraces Process and extract stacktraces (default=true) 42 | ``` 43 | 44 | ## Quickstart 45 | Run the tool against a sample bug report: 46 | 47 | ``` 48 | gradle run --args="demo/demo0001.txt" 49 | 50 | Task :run 51 | Extracted Structural Elements from demo/demo0001.txt 52 | 0 Patches 53 | 2 Stack Traces 54 | 4 Source Code Fragments 55 | 1 Enumerations 56 | Writing Cleaned Output 57 | Writing XML Output 58 | ``` 59 | 60 | This will produce two files: 61 | - `demo/demo0001.txt.cleaned` which contains the natural langauge text with all structural elements removed. This output is useful when the goal is to apply NLP algorithms that would otherwise be applied to large chunks of non-NLP data. 62 | - `demo/demo001.txt.result.xml` which contains a machine-parseable representation of all structural elements that were found in the input. 63 | 64 | ### Example XML Output 65 | ```xml 66 | 67 | 68 | 69 | 70 | 71 | org.eclipse.core.internal.resources.ResourceException 72 | Resource /org.eclipse.debug.core/.classpath is not local. 73 | 74 | org.eclipse.core.internal.resources.Resource.checkLocal(Resource.java:313) 75 | org.eclipse.core.internal.resources.File.getContents(File.java:213) 76 | org.eclipse.jdt.internal.core.Util.getResourceContentsAsByteArray(Util.java:671) 77 | org.eclipse.jdt.internal.core.JavaProject.getSharedProperty(JavaProject.java:1793) 78 | org.eclipse.jdt.internal.core.JavaProject.readClasspathFile(JavaProject.java:2089) 79 | org.eclipse.jdt.internal.core.JavaProject.getRawClasspath(JavaProject.java:1579) 80 | org.eclipse.jdt.internal.core.search.indexing.IndexAllProject.execute(IndexAllProject.java:77) 81 | org.eclipse.jdt.internal.core.search.processing.JobManager.run(JobManager.java:371) 82 | 83 | 84 | 85 | org.eclipse.core.internal.resources.ResourceException 86 | Resource /org.eclipse.jdt.launching/.classpath is not local. 87 | 88 | org.eclipse.core.internal.resources.Resource.checkLocal(Resource.java:307) 89 | org.eclipse.core.internal.resources.File.getContents(File.java:213) 90 | org.eclipse.jdt.internal.core.Util.getResourceContentsAsByteArray(Util.java:677) 91 | org.eclipse.jdt.internal.core.JavaProject.getSharedProperty(JavaProject.java:1809) 92 | org.eclipse.jdt.internal.core.JavaProject.readClasspathFile(JavaProject.java:2105) 93 | org.eclipse.jdt.internal.core.JavaProject.getRawClasspath(JavaProject.java:1593) 94 | org.eclipse.jdt.internal.core.JavaProject.getRawClasspath(JavaProject.java:1583) 95 | org.eclipse.jdt.internal.core.JavaProject.getOutputLocation(JavaProject.java:1375) 96 | org.eclipse.jdt.internal.core.search.indexing.IndexAllProject.execute(IndexAllProject.java:90) 97 | org.eclipse.jdt.internal.core.search.processing.JobManager.run(JobManager.java:375) 98 | java.lang.Thread.run(Thread.java:536) 99 | 100 | 101 | 102 | 103 | 104 | 105 | (monitor,1)); 106 | 107 | 108 | 109 | if (isJavaProject) { 110 | /*IJavaProject jProject = JavaCore.create(project); 111 | if (jProject.getRawClasspath() != null 112 | && jProject.getRawClasspath().length > 0) 113 | jProject.setRawClasspath(new IClasspathEntry[0], monitor);*/ 114 | modelIds.add(model.getPluginBase().getId()); 115 | } 116 | 117 | 118 | 119 | if (isJavaProject) { 120 | IJavaProject jProject = JavaCore.create(project); 121 | jProject.setRawClasspath(new IClasspathEntry[0], project.getFullPath(), 122 | monitor); 123 | modelIds.add(model.getPluginBase().getId()); 124 | } 125 | 126 | 127 | 128 | if (isJavaProject) { 129 | IJavaProject jProject = JavaCore.create(project); 130 | jProject.setRawClasspath(new IClasspathEntry[0], project.getFullPath(), 131 | monitor); 132 | modelIds.add(model.getPluginBase().getId()); 133 | } 134 | 135 | 136 | 137 | 138 | 139 | 1. If autobuilding is on, we turn it off. 140 | 141 | 2. We import all the plug-ins selected in the import wizard and create a Java 142 | project for each plug-in that contains libraries. Note that at this step, we 143 | used to clear the classpath of the freshly created Java project because we 144 | will correctly set it at a later step. However, just before we released 2.1, 145 | Philippe suggested in bug report 34574 that we do not flush the classpath 146 | completely. So we stopped flushing the classpath at this point, and this 147 | introduced the transient error markers that we now see in the Problems view in 148 | the middle of the operation. Since these error markers go away later in step 149 | 3 when we set the classpath, we regarded them as benign, yet still annoying, 150 | intermediary entities. This step is done in an IWorkspace.run 151 | (IWorkspaceRunnable, IProgressMonitor) operation. 152 | 153 | 3. We set the classpath of all the projects that were succesfully imported 154 | into the workspace. This step has to be done in a subsequent IWorkspace.run 155 | (IWorkspaceRunnable, IProgressMonitor) operation for an accurate classpath 156 | computation. i.e. the Java projects from step 2 have to become part of the 157 | workspace before we set their classpath. 158 | 159 | 4. If we had turned autobuilding off in step 1, we turn it back on and invoke 160 | a build via PDEPlugin.getWorkspace().build 161 | (IncrementalProjectBuilder.INCREMENTAL_BUILD,new SubProgressMonitor 162 | 163 | 164 | 165 | 166 | ``` 167 | 168 | ## Building 169 | Building the infoZilla tool requires `gradle` 4 or newer. Run `gradle tasks` for an overview. 170 | 171 | 172 | ## Citing 173 | If you like the tool and find it useful, feel free to cite the original research work: 174 | ``` 175 | @conference {972, 176 | title = {Extracting structural information from bug reports}, 177 | booktitle = {Proceedings of the 2008 international workshop on Mining software repositories - MSR {\textquoteright}08}, 178 | year = {2008}, 179 | month = {05/2008}, 180 | pages = {27-30}, 181 | publisher = {ACM Press}, 182 | organization = {ACM Press}, 183 | address = {New York, New York, USA}, 184 | abstract = {In software engineering experiments, the description of bug reports is typically treated as natural language text, although it often contains stack traces, source code, and patches. Neglecting such structural elements is a loss of valuable information; structure usually leads to a better performance of machine learning approaches. In this paper, we present a tool called infoZilla that detects structural elements from bug reports with near perfect accuracy and allows us to extract them. We anticipate that infoZilla can be used to leverage data from bug reports at a different granularity level that can facilitate interesting research in the future.}, 185 | keywords = {bug reports, eclipse, enumerations, infozilla, natural language, patches, source code, stack trace}, 186 | isbn = {9781605580241}, 187 | doi = {10.1145/1370750.1370757}, 188 | attachments = {https://flosshub.org/sites/flosshub.org/files/p27-bettenburg.pdf}, 189 | author = {Premraj, Rahul and Zimmermann, Thomas and Kim, Sunghun and Bettenburg, Nicolas} 190 | } 191 | 192 | ``` 193 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/helpers/DataExportUtility.java: -------------------------------------------------------------------------------- 1 | /** 2 | * DataExportUtility.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.helpers; 6 | 7 | import java.io.BufferedWriter; 8 | import java.io.IOException; 9 | import java.sql.Timestamp; 10 | import java.util.List; 11 | 12 | import org.jdom.Attribute; 13 | import org.jdom.Element; 14 | 15 | import io.kuy.infozilla.elements.enumeration.Enumeration; 16 | import io.kuy.infozilla.elements.patch.Patch; 17 | import io.kuy.infozilla.elements.patch.PatchHunk; 18 | import io.kuy.infozilla.elements.sourcecode.java.CodeRegion; 19 | import io.kuy.infozilla.elements.stacktrace.java.StackTrace; 20 | 21 | public class DataExportUtility { 22 | 23 | 24 | /** 25 | * Get an XML Export of a list of Stack Traces 26 | * @param traces a List of {@link StackTrace} that should be exported as a new XML node 27 | * @return an Element "Stack Traces" containing an XML Export of the given stack traces. 28 | */ 29 | public static final Element getXMLExportOfStackTraces(List traces, boolean withFrames, Timestamp ts) { 30 | Element rootE = new Element("Stacktraces"); 31 | rootE.setAttribute(new Attribute("amount", Integer.toString(traces.size()))); 32 | 33 | // Add each Stack Traces to the JDOM Tree 34 | for (StackTrace trace : traces) { 35 | 36 | // Distinguish between trace and cause 37 | Element traceE; 38 | if (trace.isCause()) 39 | traceE = new Element("Cause"); 40 | else 41 | traceE = new Element("Stacktrace"); 42 | 43 | traceE.setAttribute(new Attribute("timestamp", Long.toString(ts.getTime()))); 44 | // Add the Originating Exception 45 | Element exceptionE = new Element("Exception"); 46 | exceptionE.setText(trace.getException()); 47 | traceE.addContent(exceptionE); 48 | 49 | // As well as the Reason 50 | Element reasonE = new Element("Reason"); 51 | reasonE.setText(trace.getReason()); 52 | traceE.addContent(reasonE); 53 | 54 | if (withFrames) { 55 | // And last the Stack Frames (call stack) 56 | Element framesE = new Element("Frames"); 57 | int depth = 0; 58 | for (String frame : trace.getFrames()) { 59 | Element frameE = new Element("Frame"); 60 | frameE.setAttribute(new Attribute("depth", (Integer.toString(depth)))); 61 | frameE.setText(frame); 62 | framesE.addContent(frameE); 63 | depth++; 64 | } 65 | traceE.addContent(framesE); 66 | } 67 | 68 | // Add this Stack Trace to the Root Element 69 | rootE.addContent(traceE); 70 | } 71 | 72 | // Return the set of Stack Traces 73 | return rootE; 74 | } 75 | 76 | 77 | public static final Element getXMLExportOfStackTrace(StackTrace trace, boolean withFrames, Timestamp ts) { 78 | Element rootE = new Element("Stacktrace"); 79 | 80 | // Distinguish between trace and cause 81 | Element traceE; 82 | if (trace.isCause()) 83 | traceE = new Element("Cause"); 84 | else 85 | traceE = new Element("Stacktrace"); 86 | 87 | traceE.setAttribute(new Attribute("timestamp", Long.toString(ts.getTime()))); 88 | // Add the Originating Exception 89 | Element exceptionE = new Element("Exception"); 90 | exceptionE.setText(trace.getException()); 91 | traceE.addContent(exceptionE); 92 | 93 | // As well as the Reason 94 | Element reasonE = new Element("Reason"); 95 | reasonE.setText(trace.getReason()); 96 | traceE.addContent(reasonE); 97 | 98 | if (withFrames) { 99 | // And last the Stack Frames (call stack) 100 | Element framesE = new Element("Frames"); 101 | int depth = 0; 102 | for (String frame : trace.getFrames()) { 103 | Element frameE = new Element("Frame"); 104 | frameE.setAttribute(new Attribute("depth", (Integer.toString(depth)))); 105 | frameE.setText(frame); 106 | framesE.addContent(frameE); 107 | depth++; 108 | } 109 | traceE.addContent(framesE); 110 | } 111 | 112 | // Add this Stack Trace to the Root Element 113 | rootE.addContent(traceE); 114 | 115 | 116 | // Return the set of Stack Traces 117 | return rootE; 118 | } 119 | 120 | 121 | /** 122 | * Get an XML Export of a list of Patches 123 | * @param patches a list of {@link Patch} that should be exported as new XML node 124 | * @param withHunks a boolean value, true if we want complete Hunk output, false if this is not desired. 125 | * @return an Element "Patches" containing an XML Export of the given patches. 126 | */ 127 | public static final Element getXMLExportOfPatches(List patches, boolean withHunks) { 128 | Element rootE = new Element("Patches"); 129 | rootE.setAttribute(new Attribute("amount", Integer.toString(patches.size()))); 130 | 131 | // Add each Patch to the JDOM Tree 132 | for (Patch patch : patches) { 133 | Element patchE = new Element("Patch"); 134 | 135 | // Add the index 136 | Element indexE = new Element("index"); 137 | indexE.setText(patch.getIndex()); 138 | patchE.addContent(indexE); 139 | 140 | // Add the original File 141 | Element origE = new Element("original_file"); 142 | origE.setText(patch.getOriginalFile()); 143 | patchE.addContent(origE); 144 | 145 | // Add the modified File 146 | Element modE = new Element("modified_file"); 147 | modE.setText(patch.getModifiedFile()); 148 | patchE.addContent(modE); 149 | 150 | // Add the original header <---- This is meant for Debugging 151 | //Element oheadE = new Element("original_header"); 152 | //oheadE.setText(patch.getHeader()); 153 | //patchE.addContent(oheadE); 154 | 155 | // Add the list of Patch Hunks if we want so 156 | if (withHunks) { 157 | Element hunksE = new Element("Hunks"); 158 | for (PatchHunk hunk : patch.getHunks()) { 159 | Element hunkE = new Element("hunk"); 160 | hunkE.setText(hunk.getText()); 161 | hunksE.addContent(hunkE); 162 | } 163 | patchE.addContent(hunksE); 164 | } 165 | 166 | // Finally add the patch to the root Element 167 | rootE.addContent(patchE); 168 | } 169 | 170 | // Return the export 171 | return rootE; 172 | } 173 | 174 | 175 | /** 176 | * Get an XML Export of a list of Source Code Regions (as provided by FilterSourceCode classes) 177 | * @param coderegions a list of {@CodeRegion}s that should be exported as new XML node 178 | * @param withCode a boolean value, true if we want complete source code text, false if this is not desired. 179 | * @return an Element "Source Code Regions" containing an XML Export of the given Code Regions. 180 | */ 181 | public static final Element getXMLExportOfSourceCode(List coderegions, boolean withCode) { 182 | Element rootE = new Element("SourceCodeRegions"); 183 | rootE.setAttribute(new Attribute("amount", Integer.toString(coderegions.size()))); 184 | 185 | for (CodeRegion region : coderegions) { 186 | Element regionE = new Element("source_code"); 187 | regionE.setAttribute(new Attribute("type", region.keyword)); 188 | 189 | Element locationE = new Element("location"); 190 | locationE.setAttribute(new Attribute("start", Integer.toString(region.start))); 191 | locationE.setAttribute(new Attribute("end", Integer.toString(region.end))); 192 | 193 | regionE.addContent(locationE); 194 | 195 | if (withCode) { 196 | Element codeE = new Element("code"); 197 | codeE.setText(region.text); 198 | regionE.addContent(codeE); 199 | } 200 | 201 | rootE.addContent(regionE); 202 | } 203 | return rootE; 204 | } 205 | 206 | 207 | /** 208 | * Get an XML Export of a list of Enumeration (as provided by the FilterEnumeration classes) 209 | * @param enumerations a list of {@link Enumeration}s that should be exported as new XML node 210 | * @param withLines a boolean value, true if we want complete enumeration lines 211 | * @return an Element "Enumerations" containing an XML Export of the given Enumerations 212 | */ 213 | public static final Element getXMLExportOfEnumerations(List enumerations, boolean withLines) { 214 | Element rootE = new Element("Enumerations"); 215 | rootE.setAttribute(new Attribute("amount", Integer.toString(enumerations.size()))); 216 | 217 | for (Enumeration enu : enumerations) { 218 | Element enumE = new Element("Enumeration"); 219 | enumE.setAttribute(new Attribute("lines", Integer.toString(enu.getEnumeration_items().size()))); 220 | 221 | if (withLines) { 222 | Element linesE = new Element("Lines"); 223 | for (String line : enu.getEnumeration_items()) { 224 | Element lineE = new Element("Line"); 225 | lineE.setText(line); 226 | linesE.addContent(lineE); 227 | } 228 | enumE.addContent(linesE); 229 | } 230 | 231 | rootE.addContent(enumE); 232 | } 233 | 234 | return rootE; 235 | } 236 | 237 | 238 | /** 239 | * Write a CSV line to a BufferedWriter stream 240 | * @param writer The writer to write to 241 | * @param bug_id the bug report id 242 | * @param foundStackTrace if a stacktrace was found 243 | * @param foundPatch if a patch was found 244 | * @param foundSource if source code was found 245 | * @param foundEnum if enumerations were found 246 | */ 247 | public static void writeCSV(BufferedWriter writer, 248 | int bug_id, 249 | boolean foundStackTrace, 250 | boolean foundPatch, 251 | boolean foundSource, 252 | boolean foundEnum) { 253 | try { 254 | 255 | 256 | writer.write(Integer.toString(bug_id) + "," 257 | + getIntFromBoolean(foundStackTrace) + "," 258 | + getIntFromBoolean(foundPatch) + "," 259 | + getIntFromBoolean(foundSource) + "," 260 | + getIntFromBoolean(foundEnum) + System.getProperty("line.separator")); 261 | } catch (IOException e) { 262 | System.out.println("Could not write CSV file!"); 263 | System.out.println(e.getMessage()); 264 | System.exit(1); 265 | } 266 | } 267 | 268 | /** 269 | * Write the Results of the Duplicates analysis to a BufferedWriter Stream 270 | * @param writer the writer to write to 271 | * @param dupe_id the id of the duplicate report 272 | * @param original_id the id of the coupled original report 273 | * @param opa the amount of patches in the original report 274 | * @param ost the amount of stack traces in the original report 275 | * @param oso the amount of source code in the original report 276 | * @param oen the amount of enumerations in the original report 277 | * @param dpa the amount of patches in the duplicate report 278 | * @param dst the amount of stack traces in the duplicate report 279 | * @param dso the amount of source code in the duplicate report 280 | * @param den the amount of enumerations in the duplicate report 281 | */ 282 | public static void writeCSVDuplicateAnalysis(BufferedWriter writer, 283 | int dupe_id, int original_id, int opa, int ost, int oso, int oen, 284 | int dpa, int dst, int dso, int den) { 285 | try { 286 | 287 | writer.write(dupe_id + "," 288 | + original_id + "," 289 | + opa + "," 290 | + ost + "," 291 | + oso + "," 292 | + oen + "," 293 | + dpa + "," 294 | + dst + "," 295 | + dso + "," 296 | + den 297 | + System.getProperty("line.separator")); 298 | 299 | } catch (IOException e) { 300 | System.out.println("Could not write CSV file!"); 301 | System.out.println(e.getMessage()); 302 | System.exit(1); 303 | } 304 | } 305 | 306 | 307 | /** 308 | * Helper Function to convert boolean to int 309 | * @param b a boolean value 310 | * @return 0 if false, 1 if true 311 | */ 312 | private static int getIntFromBoolean(boolean b) { 313 | if (b) 314 | return 1; 315 | else 316 | return 0; 317 | } 318 | 319 | } 320 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/FilterStackTraceJAVA.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FilterStackTraceJAVA.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.regex.MatchResult; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | import io.kuy.infozilla.elements.stacktrace.java.StackTrace; 14 | import io.kuy.infozilla.helpers.RegExHelper; 15 | 16 | public class FilterStackTraceJAVA implements IFilter{ 17 | 18 | private FilterTextRemover textRemover; 19 | 20 | // Define a reusable Regular Expression for finding Java Stack Traces 21 | public static String JAVA_EXCEPTION = "^(([\\w<>\\$_]+\\.)+[\\w<>\\$_]+(Error|Exception){1}(\\s|:))"; 22 | public static String JAVA_REASON = "(:?.*?)(at\\s+([\\w<>\\$_]+\\.)+[\\w<>\\$_]+\\s*\\(.+?\\.java(:)?(\\d+)?\\)"; 23 | public static String JAVA_TRACE = "(\\s*?at\\s+([\\w<>\\$_\\s]+\\.)+[\\w<>\\$_\\s]+\\s*\\(.+?\\.java(:)?(\\d+)?\\))*)"; 24 | public static String JAVA_CAUSE = "(Caused by:).*?(Exception|Error)(.*?)(\\s+at.*?\\(.*?:\\d+\\))+"; 25 | public static String JAVA_STACKTRACE = JAVA_EXCEPTION + JAVA_REASON + JAVA_TRACE; 26 | private static Pattern pattern_stacktrace_java = Pattern.compile(JAVA_STACKTRACE, Pattern.DOTALL | Pattern.MULTILINE); 27 | private static Pattern pattern_cause_java = Pattern.compile(JAVA_CAUSE, Pattern.DOTALL | Pattern.MULTILINE); 28 | 29 | 30 | /** 31 | * Find StackTraces or Causes that match against our exhaustive patterns 32 | * @param s The CharSequence to look in for Stack Traces or Causes 33 | * @return A list of Matches 34 | */ 35 | private List findStackTraces(CharSequence s) { 36 | List stacktraces = new ArrayList(); 37 | 38 | for (MatchResult r : RegExHelper.findMatches(pattern_stacktrace_java, s)) { 39 | stacktraces.add(r); 40 | } 41 | 42 | for (MatchResult r: RegExHelper.findMatches(pattern_cause_java, s)) { 43 | stacktraces.add(r); 44 | } 45 | return stacktraces; 46 | } 47 | 48 | /** 49 | * This method is used to create an StackTrace object given its String representation. 50 | * @param stackTraceMatchGroup the String representing a StrackTrace Cause. 51 | * This usually comes from a RegExHelper matches' group() operation! 52 | * @return a StackTrace as represented by the given String 53 | */ 54 | private StackTrace createCause(String stackTraceMatchGroup) { 55 | String exception = ""; 56 | String reason = ""; 57 | List foundFrames = new ArrayList(); 58 | 59 | // This Pattern has: GROUP 1 GROUP 2 GROUP 3 60 | String causeException = "(Caused by:)(.*?(Error|Exception){1})(.*?)(at\\s+([\\w<>\\$_\n\r]+\\.)+[\\w<>\\$_\n\r]+\\s*\\(.+?\\.java(:)?(\\d+)?\\)(\\s*?at\\s+([\\w<>\\$_\\s]+\\.)+[\\w<>\\$_\\s]+\\s*\\(.+?\\.java(:)?(\\d+)?\\))*)"; 61 | Pattern causeEPattern = Pattern.compile(causeException, Pattern.DOTALL | Pattern.MULTILINE); 62 | 63 | // Find the Exception of this cause (which is group 2 of causeEPattern) 64 | Matcher exceptionMatcher = causeEPattern.matcher(stackTraceMatchGroup); 65 | if (exceptionMatcher.find()) { 66 | MatchResult matchResult = exceptionMatcher.toMatchResult(); 67 | 68 | exception = matchResult.group(2).trim(); 69 | reason = matchResult.group(4).trim(); 70 | 71 | // look at the frames 72 | String regexFrames = "(^\\s*?at\\s+(([\\w<>\\$_\n\r]+\\.)+[\\w<>\\$_\n\r]+\\s*\\(.*?\\)$))"; 73 | Pattern patternFrames = Pattern.compile(regexFrames, Pattern.DOTALL | Pattern.MULTILINE); 74 | 75 | // Find all frames (without the preceeding "at" ) 76 | for (MatchResult framesMatch : RegExHelper.findMatches(patternFrames, matchResult.group(5))) 77 | foundFrames.add(framesMatch.group(2).replaceAll("[\n\r]", "")); 78 | } 79 | // create a Stacktrace 80 | StackTrace trace = new StackTrace(exception, reason, foundFrames); 81 | trace.setCause(true); 82 | 83 | return(trace); 84 | } 85 | 86 | /** 87 | * This method is used to create an StackTrace object given its String representation. 88 | * @param stackTraceMatchGroup the String representing a StrackTrace. 89 | * This usually comes from a RegExHelper matches' group() operation! 90 | * @return a StackTrace as represented by the given String 91 | */ 92 | private StackTrace createTrace(String stackTraceMatchGroup) { 93 | String exception = ""; 94 | String reason = ""; 95 | List foundFrames = new ArrayList(); 96 | 97 | // This Pattern has: GROUP 1 = exception GROUP 4 = reason GROUP 5 = frames 98 | String traceException = "(([\\w<>\\$_]+\\.)+[\\w<>\\$_]+(Error|Exception){1})(.*?)(at\\s+([\\w<>\\$_\n\r]+\\.)+[\\w<>\\$_\n\r]+\\s*\\(.+?\\.java(:)?(\\d+)?\\)(\\s*?at\\s+([\\w<>\\$_\\s]+\\.)+[\\w<>\\$_\\s]+\\s*\\(.+?\\.java(:)?(\\d+)?\\))*)"; 99 | Pattern tracePattern = Pattern.compile(traceException, Pattern.DOTALL | Pattern.MULTILINE); 100 | 101 | 102 | // Find the Exception of this cause (which is group 2 of causeEPattern) 103 | Matcher exceptionMatcher = tracePattern.matcher(stackTraceMatchGroup); 104 | if (exceptionMatcher.find()) { 105 | MatchResult matchResult = exceptionMatcher.toMatchResult(); 106 | 107 | exception = matchResult.group(1).trim(); 108 | reason = matchResult.group(4).trim(); 109 | 110 | // look at the frames 111 | String regexFrames = "(^\\s*?at\\s+(([\\w<>\\$_\\s]+\\.)+[\\w<>\\$_\\s]+\\s*\\(.*?\\)$))"; 112 | Pattern patternFrames = Pattern.compile(regexFrames, Pattern.DOTALL | Pattern.MULTILINE); 113 | 114 | // Find all frames (without the preceeding "at" ) 115 | for (MatchResult framesMatch : RegExHelper.findMatches(patternFrames, matchResult.group(5))) 116 | foundFrames.add(framesMatch.group(2).replaceAll("[\n\r]", "")); 117 | } 118 | // create a Stacktrace 119 | StackTrace trace = new StackTrace(exception, reason, foundFrames); 120 | trace.setCause(false); 121 | 122 | return(trace); 123 | } 124 | 125 | /** 126 | * Get a List of StackTraces that are inside the Text s 127 | * @param inputSequence A CharSequence containing the Text to look for Stack Traces in 128 | * @return A List of StackTraces 129 | */ 130 | private List getStackTraces(CharSequence inputSequence) { 131 | List stackTraces = new ArrayList(); 132 | 133 | // Split the text sequence first by possible exception start otherwise 134 | // multiline patterns will run FOREVER! 135 | 136 | int[] possibleStart = findExceptions(inputSequence); 137 | 138 | for (int i=0; i < possibleStart.length -1; i++) { 139 | 140 | CharSequence region = inputSequence.subSequence(possibleStart[i], possibleStart[i+1] -1); 141 | List matches = findStackTraces(region); 142 | 143 | for (MatchResult match : matches) { 144 | String matchText = match.group(); 145 | 146 | // Mark this Stack Trace match for deletion 147 | int traceStart = inputSequence.toString().indexOf(matchText); 148 | int traceEnd = traceStart + matchText.length() + 1; 149 | textRemover.markForDeletion(traceStart, traceEnd); 150 | if (traceStart == 0 && traceEnd == 0) 151 | System.out.println("Critical Error in Stacktrace Filter! Could not find start and End!"); 152 | // Check if it is a cause or not 153 | if (matchText.trim().startsWith("Caused by:")) { 154 | // Create a cause 155 | StackTrace cause = createCause(matchText); 156 | // Add it to the List of Stack Traces 157 | cause.setTraceStart(traceStart); 158 | cause.setTraceEnd(traceEnd); 159 | stackTraces.add(cause); 160 | 161 | } else { 162 | // Create a trace 163 | StackTrace trace = createTrace(matchText); 164 | // Add it to the List of Stack Traces 165 | trace.setTraceStart(traceStart); 166 | trace.setTraceEnd(traceEnd); 167 | stackTraces.add(trace); 168 | } 169 | } 170 | } 171 | 172 | // And for the last region, too !!!!!!!!!!!!!!!!!!!!!!!!!!!! 173 | if (possibleStart.length > 0) { 174 | 175 | CharSequence region = inputSequence.subSequence(possibleStart[possibleStart.length-1], inputSequence.length()); 176 | List matches = findStackTraces(region); 177 | 178 | for (MatchResult match : matches) { 179 | String matchText = match.group(); 180 | 181 | // Mark this Stack Trace match for deletion 182 | int traceStart = inputSequence.toString().lastIndexOf(matchText); 183 | int traceEnd = traceStart + matchText.length(); 184 | textRemover.markForDeletion(traceStart, traceEnd); 185 | if (traceStart == 0 && traceEnd == 0) 186 | System.out.println("Critical Error in Stacktrace Filter! Could not find start and End!"); 187 | 188 | // Check if it is a cause or not 189 | if (matchText.trim().startsWith("Caused by:")) { 190 | // Create a cause 191 | StackTrace cause = createCause(matchText); 192 | // Add it to the List of Stack Traces 193 | cause.setTraceStart(traceStart); 194 | cause.setTraceEnd(traceEnd); 195 | stackTraces.add(cause); 196 | 197 | } else { 198 | // Create a trace 199 | StackTrace trace = createTrace(matchText); 200 | // Add it to the List of Stack Traces 201 | trace.setTraceStart(traceStart); 202 | trace.setTraceEnd(traceEnd); 203 | stackTraces.add(trace); 204 | } 205 | } 206 | } 207 | 208 | return stackTraces; 209 | } 210 | 211 | 212 | /** 213 | * Find a list of starting points of x.y.zException or x.y.zError 214 | * @param s The CharSequence to look inside for such starting points 215 | * @return an array of possible starting points 216 | */ 217 | private final int[] findExceptions(CharSequence s) { 218 | List exceptionList = new ArrayList(); 219 | 220 | // We match against our well known JAVA_EXCEPTION Pattern that denotes a start of an exception or error 221 | Pattern exceptionPattern = Pattern.compile(JAVA_EXCEPTION, Pattern.DOTALL | Pattern.MULTILINE); 222 | 223 | // For every match we want to add the start of that match to the list of possible starting points 224 | for (MatchResult r : RegExHelper.findMatches(exceptionPattern, s)) { 225 | 226 | // If there have previously been some starting points 227 | if (exceptionList.size() > 0){ 228 | // See if this new starting points is at least 20 lines away from the old one 229 | // Sometimes the reason contains another Exception in the first 5 lines 230 | // In this case we would otherwise omit the root exception but take the exception stated in Reason as root! 231 | String newRegion = s.subSequence(exceptionList.get(exceptionList.size()-1), r.start()).toString(); 232 | if ( newRegion.split("[\n\r]").length >= 20) 233 | exceptionList.add(Integer.valueOf(r.start())); 234 | } else { 235 | // If there had been no starting points before just add one to start with 236 | exceptionList.add(Integer.valueOf(r.start())); 237 | } 238 | } 239 | 240 | // If no region is found then go and try the whole text for exhaustive search 241 | if (exceptionList.size() == 0) 242 | exceptionList.add(Integer.valueOf(0)); 243 | 244 | // Convert the List to an array 245 | int[] results = new int[exceptionList.size()]; 246 | for (int i=0; i < exceptionList.size(); i++) 247 | results[i] = exceptionList.get(i).intValue(); 248 | 249 | return results; 250 | } 251 | 252 | // Auto-generated Message from IFilter interface 253 | public String getOutputText() { 254 | return textRemover.doDelete(); 255 | } 256 | 257 | // Auto-generated Message from IFilter interface 258 | public List runFilter(String inputText) { 259 | // Initialize TextRemover 260 | textRemover = new FilterTextRemover(inputText); 261 | // Get a Bunch of Stack Traces 262 | List foundStackTraces = getStackTraces(inputText); 263 | 264 | // Do the removal in the textRemover 265 | // ==> This is already done in getStackTraces when a MatchResult is present! 266 | 267 | // And return the found Stack Traces 268 | return foundStackTraces; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /src/main/java/io/kuy/infozilla/filters/FilterEnumeration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FilterEnumeration.java 3 | * This file is part of the infoZilla framework and tool. 4 | */ 5 | package io.kuy.infozilla.filters; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.regex.MatchResult; 10 | import java.util.regex.Pattern; 11 | 12 | import io.kuy.infozilla.elements.enumeration.Enumeration; 13 | import io.kuy.infozilla.helpers.RegExHelper; 14 | 15 | public class FilterEnumeration implements IFilter { 16 | 17 | private FilterTextRemover textRemover; 18 | private String processedText = ""; 19 | 20 | 21 | /** 22 | * Used internally to remove enumeration lines later 23 | * @param lineNum 24 | * @param text 25 | */ 26 | private void filterLine(int lineNum, String text) { 27 | // System.err.println("Called to filter line number: " + lineNum); 28 | String[] lines = text.split("[\n\r]"); 29 | 30 | // Calculate start position 31 | int start = 0; 32 | for (int i=0; i < lineNum; i++) { 33 | start = start + lines[i].length() + 1; 34 | } 35 | // Calculate end position 36 | int end = start + lines[lineNum].length(); 37 | /*System.err.println("Filtering Line: " + lines[lineNum]); 38 | System.err.println("Range: " + start + " - " + (end+1)); 39 | System.err.println(textRemover.getText().substring(start, (end+1))); 40 | System.out.println("}}}}" + textRemover.getText() + "{{{{\n\n");*/ 41 | // Mark this range for deletion 42 | textRemover.markForDeletion(start, end+1); 43 | } 44 | 45 | /** 46 | * Create an Enumeration by a given regions 47 | * @param startline the line where some enumeration starts 48 | * @param endline the last line where an enumerator symbol was found 49 | * @param text the complete text where the enumeration is in 50 | * @return an {@link Enumeration} 51 | */ 52 | private Enumeration createEnumeration(int startline, int endline, String text) { 53 | assert(startline >= 0); 54 | assert(endline >= 0); 55 | 56 | String[] lines = text.split("[\n\r]"); 57 | List enumLines = new ArrayList(); 58 | 59 | // All lines from start to end added 60 | for (int i = startline; i < endline; i++) { 61 | enumLines.add(lines[i]); 62 | filterLine(i, text); 63 | } 64 | // and all lines from endline till the end of the paragraph! 65 | int lastline = endline; 66 | for (int i = endline; i < lines.length; i++) 67 | if (lines[i].length() == 0) { 68 | break; 69 | } 70 | else { 71 | enumLines.add(lines[i]); 72 | filterLine(i, text); 73 | lastline = i; 74 | } 75 | 76 | // Calculate start position 77 | int eStart = 0; 78 | for (int i=0; i < startline; i++) { 79 | eStart = eStart + lines[i].length()+1; 80 | } 81 | // Calculate end position 82 | int eEnd = eStart; 83 | for (int i = startline; i < lastline; i++) { 84 | eEnd = eEnd + lines[i].length() +1; 85 | } 86 | eEnd = eEnd + lines[lastline].length(); 87 | 88 | Enumeration e = new Enumeration(enumLines, startline, lastline); 89 | e.setEnumStart(eStart); 90 | e.setEnumEnd(eEnd); 91 | return e; 92 | } 93 | 94 | 95 | /** 96 | * Retrieve all Enumerations that have some alphabetical letter as enumerator 97 | * @param s The text to look inside for character enumerations 98 | * @return a List of {@link Enumeration}s 99 | */ 100 | private List getCharEnums(String s) { 101 | List foundEnumerations = new ArrayList(); 102 | 103 | // RegEx for Enumerations Start 104 | // like A A. A) A.) a a. a) a.) (A) (a) etc. 105 | final String regex_EnumStart = "^\\(?([a-zA-Z])(\\.|\\.\\)|\\))[a-zA-Z \t].*"; 106 | final Pattern pattern_EnumStart = Pattern.compile(regex_EnumStart); 107 | 108 | // Split the input into lines 109 | String[] lines = s.split("[\n\r]"); 110 | 111 | int enumStart = -1; 112 | int enumEnd = -1; 113 | int previousEnumLineFound = -1; 114 | String lastFoundEnumSymbol = ""; 115 | int symbolCount = 0; 116 | 117 | // For each Line 118 | for (int i=0; i < lines.length; i++) { 119 | 120 | // Remove trailing and leading spaces 121 | String line = lines[i].trim(); 122 | 123 | // See if the line looks like some enumeration stuff 124 | if (line.matches(regex_EnumStart)) { 125 | symbolCount++; 126 | // Initialize the start of an Enumeration if none was found before! 127 | if (enumStart < 0) 128 | enumStart = i; 129 | 130 | // Store the Symbol we found this time 131 | String foundEnumSymbol = ""; 132 | for (MatchResult match : RegExHelper.findMatches(pattern_EnumStart, line)) { 133 | foundEnumSymbol = match.group(1); 134 | } 135 | 136 | // Check whether the Symbol is an increase over the previous Symbol 137 | if (foundEnumSymbol.compareTo(lastFoundEnumSymbol) > 0) { 138 | // If we have an increase, add all line between the last enum line 139 | // and the current line (i) to the array of found lines (lineBuffer) 140 | enumEnd = i; 141 | 142 | } else { 143 | // This indicates a new Enumeration has started. 144 | // So we need to add all lines starting from the previous enum line found 145 | // until the end of the paragraph (empty line); 146 | if (enumEnd < 0) 147 | enumEnd = previousEnumLineFound; 148 | if (symbolCount > 1) { 149 | Enumeration someEnum = createEnumeration(enumStart, enumEnd, s); 150 | foundEnumerations.add(someEnum); 151 | } 152 | // Reset the counters 153 | enumStart = i; 154 | enumEnd = -1; 155 | symbolCount = 0; 156 | } 157 | 158 | lastFoundEnumSymbol = foundEnumSymbol; 159 | previousEnumLineFound = i; 160 | } 161 | } 162 | 163 | // At the end put all remaining gathered lines into an Enumeration 164 | if (enumEnd < 0) 165 | enumEnd = previousEnumLineFound; 166 | 167 | if (enumStart >= 0 && symbolCount > 1) { 168 | Enumeration lastEnumeration = createEnumeration(enumStart, enumEnd, s); 169 | foundEnumerations.add(lastEnumeration); 170 | } 171 | // Return the list of found Enumerations! 172 | return foundEnumerations; 173 | } 174 | 175 | /** 176 | * Retrieve all Enumerations that have some number as enumerator 177 | * @param s The text to look inside for number enumerations 178 | * @return a List of {@link Enumeration}s 179 | */ 180 | private List getNumEnums(String s) { 181 | List foundEnumerations = new ArrayList(); 182 | 183 | // RegEx for Enumerations Start 184 | // like 1 1. 1) 1.) (1) 1- 185 | final String regex_EnumStart = "^\\(?([0-9]+)(\\.|\\.\\)|\\)|\\-)[a-zA-Z \t].*"; 186 | final Pattern pattern_EnumStart = Pattern.compile(regex_EnumStart); 187 | 188 | // Split the input into lines 189 | String[] lines = s.split("[\n\r]"); 190 | 191 | int enumStart = -1; 192 | int enumEnd = -1; 193 | int previousEnumLineFound = -1; 194 | int lastFoundEnumSymbol = -1; 195 | int symbolCount = 0; 196 | 197 | // For each Line 198 | for (int i=0; i < lines.length; i++) { 199 | 200 | // Remove trailing and leading spaces 201 | String line = lines[i].trim(); 202 | 203 | // See if the line looks like some enumeration stuff 204 | if (line.matches(regex_EnumStart)) { 205 | symbolCount++; 206 | 207 | // Initialize the start of an Enumeration if none was found before! 208 | if (enumStart < 0) 209 | enumStart = i; 210 | 211 | // Store the Symbol we found this time 212 | int foundEnumSymbol = -1; 213 | for (MatchResult match : RegExHelper.findMatches(pattern_EnumStart, line)) { 214 | try { 215 | foundEnumSymbol = Integer.valueOf(match.group(1)); 216 | } catch (NumberFormatException e) { 217 | foundEnumSymbol = Integer.MAX_VALUE; 218 | } 219 | } 220 | 221 | // Check whether the Symbol is an increase over the previous Symbol 222 | if (foundEnumSymbol > lastFoundEnumSymbol) { 223 | // If we have an increase, add all line between the last enum line 224 | // and the current line (i) to the array of found lines (lineBuffer) 225 | enumEnd = i; 226 | 227 | } else { 228 | // This indicates a new Enumeration has started. 229 | // So we need to add all lines starting from the previous enum line found 230 | // until the end of the paragraph (empty line); 231 | if (enumEnd < 0) 232 | enumEnd = previousEnumLineFound; 233 | if (symbolCount > 1) { 234 | Enumeration someEnum = createEnumeration(enumStart, enumEnd, s); 235 | foundEnumerations.add(someEnum); 236 | } 237 | // Reset the counters 238 | enumStart = i; 239 | enumEnd = -1; 240 | symbolCount = 0; 241 | } 242 | 243 | lastFoundEnumSymbol = foundEnumSymbol; 244 | previousEnumLineFound = i; 245 | } 246 | } 247 | 248 | // At the end put all remaining gathered lines into an Enumeration 249 | if (enumEnd < 0) 250 | enumEnd = previousEnumLineFound; 251 | 252 | if (enumStart >= 0 && symbolCount > 1) { 253 | Enumeration lastEnumeration = createEnumeration(enumStart, enumEnd, s); 254 | foundEnumerations.add(lastEnumeration); 255 | } 256 | // Return the list of found Enumerations! 257 | return foundEnumerations; 258 | } 259 | 260 | /** 261 | * Find all Itemizations in a given Text 262 | * @param s The text to look inside for itemizations 263 | * @return a list of {@link Enumeration}s for each Itemization 264 | */ 265 | private List getItemizations(String s) { 266 | // All found itemizations will be stored in this list 267 | List foundItemizations = new ArrayList(); 268 | 269 | // Split the input into lines 270 | String[] lines = s.split("[\n\r]"); 271 | 272 | // Setup some counters to keep track of itemizations 273 | int itemizeLineCounter = 0; 274 | int itemizeBegin = -1; 275 | int itemizeEnd = -1; 276 | int lastItemizeEnd = -1; 277 | 278 | // For each Line 279 | for (int i=0; i < lines.length; i++) { 280 | // Remove trailing and leading spaces 281 | String line = lines[i].trim(); 282 | 283 | // See if the line looks like some enumeration stuff 284 | if (line.startsWith("- ")) { 285 | // If this is a itemization line, then ... 286 | itemizeLineCounter++; 287 | 288 | // If we haven't seen any itemization before 289 | if (itemizeBegin < 0) 290 | itemizeBegin = i; 291 | 292 | lastItemizeEnd = i; 293 | 294 | } else { 295 | // If this is no itemization line, then ... 296 | 297 | // if it is an empty line then 298 | if (line.length() == 0) { 299 | // create an itemization if there are at least 2 itemize items 300 | if (itemizeBegin >= 0 && itemizeLineCounter > 1) { 301 | // Create a new itemization 302 | itemizeEnd = lastItemizeEnd; 303 | Enumeration itemization = createEnumeration(itemizeBegin, itemizeEnd, s); 304 | foundItemizations.add(itemization); 305 | } 306 | // And reset the counters 307 | itemizeBegin = -1; 308 | lastItemizeEnd = -1; 309 | itemizeEnd = -1; 310 | itemizeLineCounter = 0; 311 | } 312 | } 313 | } 314 | 315 | // If there is any remaining itemization at the end 316 | if (itemizeBegin >= 0 && itemizeLineCounter > 1) { 317 | // Create a new itemization 318 | itemizeEnd = lastItemizeEnd; 319 | Enumeration itemization = createEnumeration(itemizeBegin, itemizeEnd, s); 320 | foundItemizations.add(itemization); 321 | } 322 | 323 | // Return the list of found Enumerations! 324 | return foundItemizations; 325 | } 326 | 327 | 328 | // Still a dummy testing method 329 | public void testFilter(String s) { 330 | 331 | setProcessedText(s); 332 | 333 | List enums = getCharEnums(s); 334 | for (Enumeration e : enums) { 335 | System.out.println("### ENUMERATION ###"); 336 | System.out.println("### START=" + e.getStart()); 337 | System.out.println("### END=" + e.getEnd()); 338 | for (String line : e.getEnumeration_items()) 339 | System.out.println(">>> " + line); 340 | } 341 | 342 | System.out.println("\n\n--------------------------------"); 343 | System.out.println(getProcessedText()); 344 | System.out.println("--------------------------------\n\n"); 345 | 346 | List enums2 = getNumEnums(s); 347 | for (Enumeration e : enums2) { 348 | System.out.println("### ENUMERATION ###"); 349 | System.out.println("### START=" + e.getStart()); 350 | System.out.println("### END =" + e.getEnd()); 351 | for (String line : e.getEnumeration_items()) 352 | System.out.println(">>> " + line); 353 | } 354 | 355 | System.out.println("\n\n--------------------------------"); 356 | System.out.println(getProcessedText()); 357 | System.out.println("--------------------------------\n\n"); 358 | 359 | 360 | List enums3 = getItemizations(s); 361 | for (Enumeration e : enums3) { 362 | System.out.println("### ITEMIZATION ###"); 363 | System.out.println("### START=" + e.getStart()); 364 | System.out.println("### END=" + e.getEnd()); 365 | for (String line : e.getEnumeration_items()) 366 | System.out.println(">>> " + line); 367 | } 368 | 369 | System.out.println("\n\n--------------------------------"); 370 | System.out.println(getProcessedText()); 371 | System.out.println("--------------------------------\n\n"); 372 | 373 | } 374 | 375 | /** 376 | * Runs this method to extract all Character- and Number Enumerations as well as Itemizations 377 | * @param s The Text to look inside for enumerations and itemizations 378 | * @return a List of all {@Enumeration}s found. 379 | */ 380 | private List getEnumerationsAndItemizations(String s) { 381 | // Create an empty list to put the extracted enumerations in 382 | List enumerations = new ArrayList(); 383 | 384 | // Initialize the text filter with the text "s" we are given 385 | this.setProcessedText(s); 386 | 387 | // Process all types of enumerations, while extracting already found items 388 | List charEnums = this.getCharEnums(s); 389 | 390 | List numEnums = this.getNumEnums(s); 391 | 392 | List itemizations = this.getItemizations(s); 393 | 394 | // All add discovered items to the final list 395 | enumerations.addAll(charEnums); 396 | enumerations.addAll(numEnums); 397 | enumerations.addAll(itemizations); 398 | 399 | // Return the list of enumerations - now this.processedText contains the final text 400 | // after all processing steps (this can be used later on) 401 | return enumerations; 402 | } 403 | 404 | /** 405 | * get the text after being processed by getEnumerationsAndItemizations() method 406 | * this is initially empty string 407 | * @return a String that contains the text after being processed. 408 | */ 409 | private String getProcessedText() { 410 | if (textRemover == null) { 411 | System.err.println("We need an Instance of FilterTextRemover first!"); 412 | System.err.println("Make sure you call setProcessedText before getProcessedText!"); 413 | System.exit(1); 414 | } 415 | this.processedText = textRemover.doDelete(); 416 | return this.processedText; 417 | } 418 | 419 | 420 | // This method shall only be used internally !! 421 | private void setProcessedText(String processedText) { 422 | this.textRemover = new FilterTextRemover(processedText); 423 | } 424 | 425 | public String getOutputText() { 426 | return getProcessedText(); 427 | } 428 | 429 | 430 | public List runFilter(String inputText) { 431 | return getEnumerationsAndItemizations(inputText); 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------