package bluej.collect;

import java.io.File;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import bluej.Boot;
import bluej.compiler.CompileInputFile;
import bluej.compiler.CompileReason;
import bluej.editor.stride.FrameCatalogue;
import bluej.extensions.SourceType;
import bluej.pkgmgr.target.ClassTarget.SourceFileInfo;
import bluej.stride.generic.Frame;
import org.apache.http.entity.mime.MultipartEntity;

import threadchecker.OnThread;
import threadchecker.Tag;
import bluej.Config;
import bluej.collect.CollectUtility.ProjectDetails;
import bluej.collect.DataCollector.NamedTyped;
import bluej.compiler.Diagnostic;
import bluej.debugger.DebuggerTestResult;
import bluej.debugger.ExceptionDescription;
import bluej.debugger.SourceLocation;
import bluej.debugmgr.inspector.ClassInspector;
import bluej.debugmgr.inspector.Inspector;
import bluej.debugmgr.inspector.ObjectInspector;
import bluej.extmgr.ExtensionWrapper;
import bluej.groupwork.Repository;
import bluej.pkgmgr.Package;
import bluej.pkgmgr.Project;
import bluej.pkgmgr.target.ClassTarget;
import bluej.utility.Utility;
import difflib.Delta;
import difflib.DiffUtils;
import difflib.Patch;


| DataCollector for sending off data. | | This is the actual implementation. Methods in this class are only called when data collection | is active. | | The implementation here is separate from the interface to BlueJ, which is in the DataCollector | class, to avoid creating a run-time dependency on the commonds HTTP libraries when running without | data collection enabled (eg in Greenfoot). | @OnThread(Tag.FXPlatform) public class DataCollectorImpl {
| In BlueJ, the Project holds the list of inspectors, even though really | an inspector can be traced back to being per-package. We hold this map | to record this association (which we know when an inspector is shown) | so that we can re-use it when the inspector is hidden. | private static IdentityHashMap<Inspector, Package> inspectorPackages = new IdentityHashMap<Inspector, Package>();
| Submits an event with no extra data. A useful short-hand for calling submitEvent | with no content in the event. | private static void submitEventNoData(Project project, Package pkg, EventName eventName) { submitEvent(project, pkg, eventName, new PlainEvent(new MultipartEntity())); }
| Submits an event and adds a source location. This should be used if the file | is within the project (otherwise see submitEventWithClassLocation) | | @param project | @param eventName | @param mpe You can pass null if you have no other data to give | @param sourceFile | @param lineNumber | private static void submitEventWithLocalLocation(Project project, Package pkg, EventName eventName, MultipartEntity mpe, File sourceFile, int lineNumber) { if (mpe == null) { mpe = new MultipartEntity(); } mpe.addPart("event[source_file_name]", CollectUtility.toBodyLocal(new ProjectDetails(project), sourceFile)); mpe.addPart("event[line_number]", CollectUtility.toBody(lineNumber)); submitEvent(project, pkg, eventName, new PlainEvent(mpe)); }
| Submits an event and adds a source location. This should be used if it is not | known whether the class is in the project, or whether it might be from a library | (e.g. java.io.*) or wherever. | | @param project | @param eventName | @param mpe You can pass null if you have no other data to give | @param classSourceName | @param lineNumber | private static void submitDebuggerEventWithLocation(Project project, EventName eventName, MultipartEntity mpe, SourceLocation[] stack) { if (mpe == null) { mpe = new MultipartEntity(); } DataCollectorImpl.addStackTrace(mpe, "event[stack]", stack); submitEvent(project, null, eventName, new PlainEvent(mpe)); }
| Submit an event. | | @param project the associated project (may be null) | @param pkg the associated package (may be null) | @param eventName the name of the event type | @param evt the event to be submitted | private static synchronized void submitEvent(final Project project, final Package pkg, final EventName eventName, final Event evt) { final String projectName = project == null ? null : project.getProjectName(); final String projectPathHash = project == null ? null : CollectUtility.md5Hash(project.getProjectDir().getAbsolutePath()); final String packageName = pkg == null ? null : pkg.getQualifiedName(); final String uuidCopy = DataCollector.getUserID(); final String experimentCopy = DataCollector.getExperimentIdentifier(); final String participantCopy = DataCollector.getParticipantIdentifier();
| Wrap the Event we've been given to add the other normal expected fields: | DataSubmitter.submitEvent(new Event() { @Override public void success(Map<FileKey, List<String>> fileVersions) { evt.success(fileVersions); } @Override @OnThread(Tag.Worker) public MultipartEntity makeData(int sequenceNum, Map<FileKey, List<String>> fileVersions) { MultipartEntity mpe = evt.makeData(sequenceNum, fileVersions); if (mpe == null) return null; mpe.addPart("user[uuid]", CollectUtility.toBody(uuidCopy)); mpe.addPart("session[id]", CollectUtility.toBody(DataCollector.getSessionUuid())); mpe.addPart("participant[experiment]", CollectUtility.toBody(experimentCopy)); mpe.addPart("participant[participant]", CollectUtility.toBody(participantCopy)); if (projectName != null) { mpe.addPart("project[name]", CollectUtility.toBody(projectName)); mpe.addPart("project[path_hash]", CollectUtility.toBody(projectPathHash)); if (packageName != null) { mpe.addPart("package[name]", CollectUtility.toBody(packageName)); } } mpe.addPart("event[source_time]", CollectUtility.toBody(DateFormat.getDateTimeInstance().format(new Date()))); mpe.addPart("event[name]", CollectUtility.toBody(eventName.getName())); mpe.addPart("event[sequence_id]", CollectUtility.toBody(Integer.toString(sequenceNum))); return mpe; } }); } public static void compiled(Project proj, Package pkg, CompileInputFile[] sources, List<DiagnosticWithShown> diagnostics, boolean success, CompileReason compileReason, int compileSequence) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[compile_success]", CollectUtility.toBody(success)); mpe.addPart("event[compile_reason]", CollectUtility.toBody(compileReason.getServerString())); if (compileSequence != -1) { mpe.addPart("event[compile_sequence]", CollectUtility.toBody(compileSequence)); } ProjectDetails projDetails = new ProjectDetails(proj); for (CompileInputFile src : sources) { mpe.addPart("event[compile_input][][source_file_name]", CollectUtility.toBody(CollectUtility.toPath(projDetails, src.getUserSourceFile()))); } for (DiagnosticWithShown dws : diagnostics) { final Diagnostic d = dws.getDiagnostic(); mpe.addPart("event[compile_output][][is_error]", CollectUtility.toBody(d.getType() == Diagnostic.ERROR)); mpe.addPart("event[compile_output][][message]", CollectUtility.toBody(d.getMessage())); mpe.addPart("event[compile_output][][session_sequence]", CollectUtility.toBody(d.getIdentifier())); mpe.addPart("event[compile_output][][origin]", CollectUtility.toBody(d.getOrigin())); if (d.getFileName() != null) { if (d.getStartLine() >= 1) mpe.addPart("event[compile_output][][start_line]", CollectUtility.toBody(d.getStartLine())); if (d.getEndLine() >= 1) mpe.addPart("event[compile_output][][end_line]", CollectUtility.toBody(d.getEndLine())); if (d.getStartColumn() >= 1) mpe.addPart("event[compile_output][][start_column]", CollectUtility.toBody(d.getStartColumn())); if (d.getEndColumn() >= 1) mpe.addPart("event[compile_output][][end_column]", CollectUtility.toBody(d.getEndColumn())); if (d.getXPath() != null) { mpe.addPart("event[compile_output][][xpath]", CollectUtility.toBody(d.getXPath())); if (d.getXmlStart() != -1) mpe.addPart("event[compile_output][][xml_start]", CollectUtility.toBody(d.getXmlStart())); if (d.getXmlEnd() != -1) mpe.addPart("event[compile_output][][xml_end]", CollectUtility.toBody(d.getXmlEnd())); } String relative = CollectUtility.toPath(projDetails, dws.getUserFileName()); mpe.addPart("event[compile_output][][source_file_name]", CollectUtility.toBody(relative)); } } submitEvent(proj, pkg, EventName.COMPILE, new PlainEvent(mpe)); } public static void bluejOpened(String osVersion, String javaVersion, String bluejVersion, String interfaceLanguage, List<ExtensionWrapper> extensions) { if (Config.isGreenfoot() && !Boot.isTrialRecording()) return; DataSubmitter.initSequence(); MultipartEntity mpe = new MultipartEntity(); mpe.addPart("installation[operating_system]", CollectUtility.toBody(osVersion)); mpe.addPart("installation[java_version]", CollectUtility.toBody(javaVersion)); mpe.addPart("installation[bluej_version]", CollectUtility.toBody(bluejVersion)); mpe.addPart("installation[interface_language]", CollectUtility.toBody(interfaceLanguage)); addExtensions(mpe, extensions); submitEvent(null, null, EventName.BLUEJ_START, new PlainEvent(mpe)); } private static void addExtensions(MultipartEntity mpe, List<ExtensionWrapper> extensions) { for (ExtensionWrapper ext : extensions) { mpe.addPart("extensions[][name]", CollectUtility.toBody(ext.safeGetExtensionName())); mpe.addPart("extensions[][version]", CollectUtility.toBody(ext.safeGetExtensionVersion())); } } public static void projectOpened(Project proj, List<ExtensionWrapper> projectExtensions) { MultipartEntity mpe = new MultipartEntity(); addExtensions(mpe, projectExtensions); submitEventNoData(proj, null, EventName.PROJECT_OPENING); } public static void projectClosed(Project proj) { submitEventNoData(proj, null, EventName.PROJECT_CLOSING); }
| Records the EventName.PACKAGE_OPENING event for the given package, including sending relevant source histories. | public static void packageOpened(Package pkg) { addCompleteFiles(pkg, EventName.PACKAGE_OPENING, pkg.getClassTargets(), null); }
| Sends an event with a set of complete files. | | @param pkg The package that the set of files all belong to. | @param eventName The event name to send with the event | @param classTargets The class targets of the files to send. | If there are Stride files, both Stride and Java | will be sent. | @param extra The extra action to run on the event before sending, | e.g. add an extra field to send. May be null (meaning no | extra action to run). | private static void addCompleteFiles(Package pkg, EventName eventName, List<ClassTarget> classTargets, Consumer<MultipartEntity> extra) { final MultipartEntity mpe = new MultipartEntity(); if (extra != null) { extra.accept(mpe); } final ProjectDetails proj = new ProjectDetails(pkg.getProject()); final Map<FileKey, List<String>> versions = new HashMap<>(); for (ClassTarget ct : classTargets) { for (SourceFileInfo fileInfo : ct.getAllSourceFilesJavaLast()) { String relative = CollectUtility.toPath(proj, fileInfo.file); mpe.addPart("project[source_files][][name]", CollectUtility.toBody(relative)); switch (fileInfo.sourceType) { case Java: mpe.addPart("project[source_files][][source_type]", CollectUtility.toBody("java")); break; case Stride: mpe.addPart("project[source_files][][source_type]", CollectUtility.toBody("stride")); break; } String anonymisedContent = CollectUtility.readFileAndAnonymise(proj, fileInfo.file); String generatedFrom = null; if (fileInfo.sourceType == SourceType.Java && ct.getSourceType() == SourceType.Stride) { generatedFrom = CollectUtility.toPath(proj, ct.getSourceFile()); if (anonymisedContent == null) anonymisedContent = ""; } if (anonymisedContent != null) { addSourceHistoryItem(mpe, relative, "complete", anonymisedContent, generatedFrom); versions.put(new FileKey(proj, relative), Arrays.asList(Utility.splitLines(anonymisedContent))); } } } submitEvent(pkg.getProject(), pkg, eventName, new Event() { @Override public void success(Map<FileKey, List<String>> fileVersions) { fileVersions.putAll(versions); } @Override @OnThread(Tag.Worker) public MultipartEntity makeData(int sequenceNum, Map<FileKey, List<String>> fileVersions) { return mpe; } }); }
| Adds a source history item to the MPE | @param mpe The MPE we're sending to the server | @param relativeName The name of the file (relative to project root) | @param anonymisedContent The anonymised content of the file or diff. May null for some types (e.g. file deletion) | @param generatedFrom The Stride file this Java file was generated from. May be null if this is a Stride file, or a Java file that has no Stride. | @OnThread(Tag.Any) private static void addSourceHistoryItem(MultipartEntity mpe, String relativeName, String type, String anonymisedContent, String generatedFrom) { mpe.addPart("source_histories[][source_history_type]", CollectUtility.toBody(type)); mpe.addPart("source_histories[][name]", CollectUtility.toBody(relativeName)); if (generatedFrom != null) { mpe.addPart("source_histories[][generated_from]", CollectUtility.toBody(generatedFrom)); } if (anonymisedContent != null) { mpe.addPart("source_histories[][content]", CollectUtility.toBody(anonymisedContent)); } }
| Records the EventName.PACKAGE_CLOSING event for the given package, including sending relevant source histories. | public static void packageClosed(Package pkg) { addCompleteFiles(pkg, EventName.PACKAGE_CLOSING, pkg.getClassTargets(), mpe -> { mpe.addPart("event[has_hash]", CollectUtility.toBody(true)); }); } public static void bluejClosed() { submitEventNoData(null, null, EventName.BLUEJ_FINISH); DataSubmitter.waitForQueueFlush(1000); } public static void restartVM(Project project) { submitEventNoData(project, null, EventName.RESETTING_VM); } static class EditedFileInfo { private final String editType; private final File path; private final String source; private final boolean includeOneLineEdits; private final File generatedFrom; private final StrideEditReason strideEditReason; private FileKey fileKey; private List<String> anonSource; public boolean dontSend = false; EditedFileInfo(String editType, File path, String source, boolean includeOneLineEdits, File generatedFrom, StrideEditReason strideEditReason) { this.editType = editType; this.path = path; this.source = source; this.includeOneLineEdits = includeOneLineEdits; this.generatedFrom = generatedFrom; this.strideEditReason = strideEditReason; } } p.public static void edit(final Package pkg, List<EditedFileInfo> editedFiles) { final Project proj = pkg.getProject(); final ProjectDetails projDetails = new ProjectDetails(proj); for (EditedFileInfo editedFile : editedFiles) { editedFile.fileKey = new FileKey(projDetails, CollectUtility.toPath(projDetails, editedFile.path)); editedFile.anonSource = Arrays.asList(Utility.splitLines(CodeAnonymiser.anonymise(editedFile.source))); } submitEvent(proj, pkg, EventName.EDIT, new Event() { private boolean isOneLineDiff(Patch patch) { if (patch.getDeltas().size() > 1) return false; Delta theDelta = patch.getDeltas().get(0); return theDelta.getOriginal().size() == 1 && theDelta.getRevised().size() == 1; } @Override @OnThread(Tag.Worker) public MultipartEntity makeData(int sequenceNum, Map<FileKey, List<String>> fileVersions) { MultipartEntity mpe = new MultipartEntity(); for (EditedFileInfo editedFile : editedFiles) { List<String> previousDoc = fileVersions.get(editedFile.fileKey); if (previousDoc == null) previousDoc = new ArrayList<String>(); Patch patch = DiffUtils.diff(previousDoc, editedFile.anonSource); if (patch.getDeltas().isEmpty() || (isOneLineDiff(patch) && !editedFile.includeOneLineEdits)) { editedFile.dontSend = true; continue; } String diff = makeDiff(patch); addSourceHistoryItem(mpe, CollectUtility.toPath(projDetails, editedFile.path), editedFile.editType, diff, editedFile.generatedFrom == null ? null : CollectUtility.toPath(projDetails, editedFile.generatedFrom)); if (editedFile.strideEditReason != null && editedFile.strideEditReason.getText() != null) { mpe.addPart("source_histories[][reason]", CollectUtility.toBody(editedFile.strideEditReason.getText())); } } if (editedFiles.stream().allMatch(f -> f.dontSend)) { return null; } else { return mpe; } } @Override public void success(Map<FileKey, List<String>> fileVersions) { for (EditedFileInfo editedFile : editedFiles) { if (!editedFile.dontSend) { fileVersions.put(editedFile.fileKey, editedFile.anonSource); } } } }); } @SuppressWarnings("unchecked") @OnThread(Tag.Any) protected static String makeDiff(Patch patch) { StringBuilder diff = new StringBuilder(); for (Delta delta: patch.getDeltas()) { int srcLine, srcSize, destLine, destSize; srcSize = delta.getOriginal().size(); destSize = delta.getRevised().size(); if (srcSize > 0) { srcLine = delta.getOriginal().getPosition() + 1; destLine = delta.getRevised().getPosition() + 1; } else { srcLine = delta.getOriginal().getPosition(); destLine = delta.getRevised().getPosition(); } diff.append("@@ -" + srcLine + "," + srcSize + " +" + destLine + "," + destSize + " @@\n"); for (String l : (List<String>)delta.getOriginal().getLines()) { diff.append("-" + l + "\n"); } for (String l : (List<String>)delta.getRevised().getLines()) { diff.append("+" + l + "\n"); } } return diff.toString(); } public static void debuggerTerminate(Project project) { submitEventNoData(project, null, EventName.DEBUGGER_TERMINATE); } public static void debuggerChangeVisible(Project project, boolean newVis) { submitEventNoData(project, null, newVis ? EventName.DEBUGGER_OPEN : EventName.DEBUGGER_CLOSE); } public static void debuggerBreakpointToggle(Package pkg, File sourceFile, int lineNumber, boolean newState) { submitEventWithLocalLocation(pkg.getProject(), pkg, newState ? EventName.DEBUGGER_BREAKPOINT_ADD : EventName.DEBUGGER_BREAKPOINT_REMOVE, null, sourceFile, lineNumber); } public static void debuggerContinue(Project project, String threadName) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[thread_name]", CollectUtility.toBody(threadName)); submitEvent(project, null, EventName.DEBUGGER_CONTINUE, new PlainEvent(mpe)); } public static void debuggerHalt(Project project, String threadName, SourceLocation[] stack) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[thread_name]", CollectUtility.toBody(threadName)); submitDebuggerEventWithLocation(project, EventName.DEBUGGER_HALT, mpe, stack); } public static void debuggerStepInto(Project project, String threadName, SourceLocation[] stack) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[thread_name]", CollectUtility.toBody(threadName)); submitDebuggerEventWithLocation(project, EventName.DEBUGGER_STEP_INTO, mpe, stack); } public static void debuggerStepOver(Project project, String threadName, SourceLocation[] stack) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[thread_name]", CollectUtility.toBody(threadName)); submitDebuggerEventWithLocation(project, EventName.DEBUGGER_STEP_OVER, mpe, stack); } public static void debuggerHitBreakpoint(Project project, String threadName, SourceLocation[] stack) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[thread_name]", CollectUtility.toBody(threadName)); submitDebuggerEventWithLocation(project, EventName.DEBUGGER_HIT_BREAKPOINT, mpe, stack); } public static void codePadSuccess(Package pkg, String command, String output) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[codepad][outcome]", CollectUtility.toBody("success")); mpe.addPart("event[codepad][command]", CollectUtility.toBody(command)); mpe.addPart("event[codepad][result]", CollectUtility.toBody(output)); submitEvent(pkg.getProject(), pkg, EventName.CODEPAD, new PlainEvent(mpe)); } public static void codePadError(Package pkg, String command, String error) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[codepad][outcome]", CollectUtility.toBody("error")); mpe.addPart("event[codepad][command]", CollectUtility.toBody(command)); mpe.addPart("event[codepad][error]", CollectUtility.toBody(error)); submitEvent(pkg.getProject(), pkg, EventName.CODEPAD, new PlainEvent(mpe)); } public static void codePadException(Package pkg, String command, String exception) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[codepad][outcome]", CollectUtility.toBody("exception")); mpe.addPart("event[codepad][command]", CollectUtility.toBody(command)); mpe.addPart("event[codepad][exception]", CollectUtility.toBody(exception)); submitEvent(pkg.getProject(), pkg, EventName.CODEPAD, new PlainEvent(mpe)); }
| Records renaming a class (Java, or Stride and its generated Java). | | @param pkg The package in which the files live. | @param oldFrameSourceFile The original Stride source file that has been deleted, | or <code>null</code> in case the source type is Java. | @param newFrameSourceFile The new created Stride source file, or <code>null</code> in case the source type is Java. | @param oldJavaSourceFile The original Java source file that has been deleted. | @param newJavaSourceFile The new created Java source file. | public static void renamedClass(Package pkg, final File oldFrameSourceFile, final File newFrameSourceFile, final File oldJavaSourceFile, final File newJavaSourceFile) { final String eventType = "rename"; final ProjectDetails projDetails = new ProjectDetails(pkg.getProject()); final boolean isFrameFile = newFrameSourceFile != null; final String oldFrameFilePath = isFrameFile ? CollectUtility.toPath(projDetails, oldFrameSourceFile) : null; final String newFrameFilePath = isFrameFile ? CollectUtility.toPath(projDetails, newFrameSourceFile) : null; final String oldJavaFilePath = CollectUtility.toPath(projDetails, oldJavaSourceFile); final String newJavaFilePath = CollectUtility.toPath(projDetails, newJavaSourceFile); MultipartEntity mpe = new MultipartEntity(); if (isFrameFile) { addSourceHistoryItem(mpe, newFrameFilePath, eventType, oldFrameFilePath, null); } addSourceHistoryItem(mpe, newJavaFilePath, eventType, oldJavaFilePath, newFrameFilePath); submitEvent(pkg.getProject(), pkg, EventName.RENAME, new PlainEvent(mpe) { @Override @OnThread(Tag.Worker) public MultipartEntity makeData(int sequenceNum, Map<FileKey, List<String>> fileVersions) { if (isFrameFile) { FileKey oldFrameKey = new FileKey(projDetails, oldFrameFilePath); FileKey newFrameKey = new FileKey(projDetails, newFrameFilePath); fileVersions.put(newFrameKey, fileVersions.get(oldFrameKey)); fileVersions.remove(oldFrameKey); } FileKey oldJavaKey = new FileKey(projDetails, oldJavaFilePath); FileKey newJavaKey = new FileKey(projDetails, newJavaFilePath); fileVersions.put(newJavaKey, fileVersions.get(oldJavaKey)); fileVersions.remove(oldJavaKey); return super.makeData(sequenceNum, fileVersions); } }); }
| Records removing class files (Java, or Stride and its generated Java). | | @param pkg The package in which the files live. | @param frameSourceFile The Stride source file, or <code>null</code> in case the source type is Java. | @param javaSourceFile The Java source file. | public static void removeClass(Package pkg, File frameSourceFile, File javaSourceFile) { final String eventType = "file_delete"; final ProjectDetails projDetails = new ProjectDetails(pkg.getProject()); final boolean isStrideFile = frameSourceFile != null; final String strideFilePath = isStrideFile ? CollectUtility.toPath(projDetails, frameSourceFile) : null; final String javaFilePath = CollectUtility.toPath(projDetails, javaSourceFile); MultipartEntity mpe = new MultipartEntity(); if (isStrideFile) { addSourceHistoryItem(mpe, strideFilePath, eventType, null, null); } addSourceHistoryItem(mpe, javaFilePath, eventType, null, strideFilePath); submitEvent(pkg.getProject(), pkg, EventName.DELETE, new PlainEvent(mpe) { @Override @OnThread(Tag.Worker) public MultipartEntity makeData(int sequenceNum, Map<FileKey, List<String>> fileVersions) { if (isStrideFile) { fileVersions.remove(new FileKey(projDetails, strideFilePath)); } fileVersions.remove(new FileKey(projDetails, javaFilePath)); return super.makeData(sequenceNum, fileVersions); } }); }
| Send a conversion event (Stride to Java, or Java to Stride) to the server. | @param pkg The package that the files live in | @param javaSourceFile The Java file involved in the conversion (may be source or destination depending on conversino direction) | @param strideSourceFile The Stride file involved in the conversion (ditto: may be source or destination) | @param strideToJava If true, conversion is Stride->Java. If false, conversion is Java->Stride. | p.public static void conversion(Package pkg, File javaSourceFile, File strideSourceFile, boolean strideToJava) { final ProjectDetails projDetails = new ProjectDetails(pkg.getProject()); MultipartEntity mpe = new MultipartEntity(); String anonStride; if (strideToJava) { addSourceHistoryItem(mpe, CollectUtility.toPath(projDetails, strideSourceFile), "file_delete", null, null); anonStride = null; } else { anonStride = CollectUtility.readFileAndAnonymise(projDetails, strideSourceFile); addSourceHistoryItem(mpe, CollectUtility.toPath(projDetails, strideSourceFile), "java_to_stride", anonStride, null); mpe.addPart("source_histories[][converted_from]", CollectUtility.toBodyLocal(projDetails, javaSourceFile)); } mpe.addPart("source_histories[][source_history_type]", CollectUtility.toBody(strideToJava ? "stride_to_java" : "diff_generated")); mpe.addPart("source_histories[][name]", CollectUtility.toBodyLocal(projDetails, javaSourceFile)); final List<String> anonJava = Arrays.asList(Utility.splitLines(CollectUtility.readFileAndAnonymise(projDetails, javaSourceFile))); if (strideToJava) { mpe.addPart("source_histories[][converted_from]", CollectUtility.toBodyLocal(projDetails, strideSourceFile)); } else { mpe.addPart("source_histories[][generated_from]", CollectUtility.toBodyLocal(projDetails, strideSourceFile)); } final FileKey strideFileKey = new FileKey(projDetails, CollectUtility.toPath(projDetails, strideSourceFile)); final FileKey javaFileKey = new FileKey(projDetails, CollectUtility.toPath(projDetails, javaSourceFile)); submitEvent(pkg.getProject(), pkg, strideToJava ? EventName.CONVERT_STRIDE_TO_JAVA : EventName.CONVERT_JAVA_TO_STRIDE, new PlainEvent(mpe) { @Override @OnThread(Tag.Worker) public MultipartEntity makeData(int sequenceNum, Map<FileKey, List<String>> fileVersions) { List<String> previousDoc = fileVersions.get(javaFileKey); if (previousDoc == null) previousDoc = new ArrayList<String>(); MultipartEntity mpe = new MultipartEntity(); Patch patch = DiffUtils.diff(previousDoc, anonJava); String diff = makeDiff(patch); mpe.addPart("source_histories[][content]", CollectUtility.toBody(diff)); fileVersions.put(javaFileKey, anonJava); if (strideToJava) { fileVersions.remove(strideFileKey); } else { fileVersions.put(strideFileKey, Arrays.asList(Utility.splitLines(anonStride))); } return super.makeData(sequenceNum, fileVersions); } }); }
| Records the EventName.ADD event, indicating that the given class was added. | @param pkg The package containing the class. | @param ct The class involved. Relevant source history (i.e. complete file) will be | sent, either just .java (for Java classes) or .stride and .java (for Stride classes) | public static void addClass(Package pkg, ClassTarget ct) { addCompleteFiles(pkg, EventName.ADD, Collections.singletonList(ct), null); } public static void openClass(Package pkg, File sourceFile) { classEvent(pkg, sourceFile, EventName.FILE_OPEN); } public static void closeClass(Package pkg, File sourceFile) { classEvent(pkg, sourceFile, EventName.FILE_CLOSE); } public static void selectClass(Package pkg, File sourceFile) { classEvent(pkg, sourceFile, EventName.FILE_SELECT); } private static void classEvent(Package pkg, File sourceFile, EventName eventName) { final MultipartEntity mpe = new MultipartEntity(); final ProjectDetails projDetails = new ProjectDetails(pkg.getProject()); mpe.addPart("event[source_file_name]", CollectUtility.toBodyLocal(projDetails, sourceFile)); submitEvent(pkg.getProject(), pkg, eventName, new PlainEvent(mpe)); } public static void teamShareProject(Project project, Repository repo) { submitEvent(project, null, EventName.VCS_SHARE, new PlainEvent(DataCollectorImpl.getRepoMPE(repo))); }
| Records a VCS push event | @param project The project which is in VCS | @param repo The repository object for VCS | @param pushedFiles The files involved in the push | public static void teamPushProject(Project project, Repository repo, Collection<File> pushedFiles) { final ProjectDetails projDetails = new ProjectDetails(project); MultipartEntity mpe = DataCollectorImpl.getRepoMPE(repo); for (File f : pushedFiles) { mpe.addPart("vcs_files[][file]", CollectUtility.toBodyLocal(projDetails, f)); } submitEvent(project, null, EventName.VCS_PUSH, new PlainEvent(mpe)); } public static void teamCommitProject(Project project, Repository repo, Collection<File> committedFiles) { final ProjectDetails projDetails = new ProjectDetails(project); MultipartEntity mpe = DataCollectorImpl.getRepoMPE(repo); for (File f : committedFiles) { mpe.addPart("vcs_files[][file]", CollectUtility.toBodyLocal(projDetails, f)); } submitEvent(project, null, EventName.VCS_COMMIT, new PlainEvent(mpe)); } public static void teamUpdateProject(Project project, Repository repo, Collection<File> updatedFiles) { final ProjectDetails projDetails = new ProjectDetails(project); MultipartEntity mpe = DataCollectorImpl.getRepoMPE(repo); for (File f : updatedFiles) { mpe.addPart("vcs_files[][file]", CollectUtility.toBodyLocal(projDetails, f)); } submitEvent(project, null, EventName.VCS_UPDATE, new PlainEvent(mpe)); } public static void teamStatusProject(Project project, Repository repo, Map<File, String> status) { final ProjectDetails projDetails = new ProjectDetails(project); MultipartEntity mpe = DataCollectorImpl.getRepoMPE(repo); for (Map.Entry<File, String> s : status.entrySet()) { mpe.addPart("vcs_files[][file]", CollectUtility.toBodyLocal(projDetails, s.getKey())); mpe.addPart("vcs_files[][status]", CollectUtility.toBody(s.getValue())); } submitEvent(project, null, EventName.VCS_STATUS, new PlainEvent(mpe)); } public static void teamHistoryProject(Project project, Repository repo) { submitEvent(project, null, EventName.VCS_HISTORY, new PlainEvent(DataCollectorImpl.getRepoMPE(repo))); } public static void showHideTerminal(Project project, boolean show) { submitEventNoData(project, null, show ? EventName.TERMINAL_OPEN : EventName.TERMINAL_CLOSE); } public static void invokeCompileError(Package pkg, String code, String compilationError) { if (! false) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[invoke][code]", CollectUtility.toBody(code)); mpe.addPart("event[invoke][result]", CollectUtility.toBody("compile_error")); mpe.addPart("event[invoke][compile_error]", CollectUtility.toBody(compilationError)); submitEvent(pkg.getProject(), pkg, EventName.INVOKE_METHOD, new PlainEvent(mpe)); } } public static void invokeMethodSuccess(Package pkg, String code, String objName, String typeName, int testIdentifier, int invocationIdentifier) { if (! false) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[invoke][code]", CollectUtility.toBody(code)); mpe.addPart("event[invoke][type_name]", CollectUtility.toBody(typeName)); mpe.addPart("event[invoke][result]", CollectUtility.toBody("success")); mpe.addPart("event[invoke][test_identifier]", CollectUtility.toBody(testIdentifier)); mpe.addPart("event[invoke][invoke_identifier]", CollectUtility.toBody(invocationIdentifier)); if (objName != null) { mpe.addPart("event[invoke][bench_object][class_name]", CollectUtility.toBody(typeName)); mpe.addPart("event[invoke][bench_object][name]", CollectUtility.toBody(objName)); } submitEvent(pkg.getProject(), pkg, EventName.INVOKE_METHOD, new PlainEvent(mpe)); } } public static void invokeMethodException(Package pkg, String code, ExceptionDescription ed) { if (! false) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[invoke][code]", CollectUtility.toBody(code)); mpe.addPart("event[invoke][result]", CollectUtility.toBody("exception")); mpe.addPart("event[invoke][exception_class]", CollectUtility.toBody(ed.getClassName())); mpe.addPart("event[invoke][exception_message]", CollectUtility.toBody(ed.getText())); DataCollectorImpl.addStackTrace(mpe, "event[invoke][exception_stack]", ed.getStack().toArray(new SourceLocation[0])); submitEvent(pkg.getProject(), pkg, EventName.INVOKE_METHOD, new PlainEvent(mpe)); } } public static void invokeMethodTerminated(Package pkg, String code) { if (! false) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[invoke][code]", CollectUtility.toBody(code)); mpe.addPart("event[invoke][result]", CollectUtility.toBody("terminated")); submitEvent(pkg.getProject(), pkg, EventName.INVOKE_METHOD, new PlainEvent(mpe)); } } public static void removeObject(Package pkg, String name) { if (! false) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[object_name]", CollectUtility.toBody(name)); submitEvent(pkg.getProject(), pkg, EventName.REMOVE_OBJECT, new PlainEvent(mpe)); } }
| A class inspector was shown. | | @param proj The project associated with the action | @param pkg The package associated with the action; may be null | @param inspector The inspector shown | @param className The name of the class associated with the inspector | public static void inspectorClassShow(Project proj, Package pkg, Inspector inspector, String className) { if (! false) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[inspect][unique]", CollectUtility.toBody(inspector.getUniqueId())); mpe.addPart("event[inspect][static_class]", CollectUtility.toBody(className)); inspectorPackages.put(inspector, pkg); submitEvent(proj, pkg, EventName.INSPECTOR_SHOW, new PlainEvent(mpe)); } } public static void inspectorObjectShow(Package pkg, Inspector inspector, String benchName, String className, String displayName) { if (! false) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[inspect][unique]", CollectUtility.toBody(inspector.getUniqueId())); mpe.addPart("event[inspect][display_name]", CollectUtility.toBody(displayName)); mpe.addPart("event[inspect][class_name]", CollectUtility.toBody(className)); if (benchName != null) { mpe.addPart("event[inspect][bench_object_name]", CollectUtility.toBody(benchName)); } inspectorPackages.put(inspector, pkg); submitEvent(pkg.getProject(), pkg, EventName.INSPECTOR_SHOW, new PlainEvent(mpe)); } } public static void inspectorHide(Project project, Inspector inspector) { if (! false) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[inspect][unique]", CollectUtility.toBody(inspector.getUniqueId())); if (inspector instanceof ClassInspector || inspector instanceof ObjectInspector) { submitEvent(project, inspectorPackages.get(inspector), EventName.INSPECTOR_HIDE, new PlainEvent(mpe)); } } } public static void benchGet(Package pkg, String benchName, String typeName, int testIdentifier) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[bench_object][class_name]", CollectUtility.toBody(typeName)); mpe.addPart("event[bench_object][name]", CollectUtility.toBody(benchName)); mpe.addPart("event[test_identifier]", CollectUtility.toBody(testIdentifier)); submitEvent(pkg.getProject(), pkg, EventName.BENCH_GET, new PlainEvent(mpe)); } public static void startTestMethod(Package pkg, int testIdentifier, File sourceFile, String testName) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[test][test_identifier]", CollectUtility.toBody(testIdentifier)); mpe.addPart("event[test][source_file]", CollectUtility.toBodyLocal(new ProjectDetails(pkg.getProject()), sourceFile)); mpe.addPart("event[test][method_name]", CollectUtility.toBody(testName)); submitEvent(pkg.getProject(), pkg, EventName.START_TEST, new PlainEvent(mpe)); } public static void cancelTestMethod(Package pkg, int testIdentifier) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[test][test_identifier]", CollectUtility.toBody(testIdentifier)); submitEvent(pkg.getProject(), pkg, EventName.CANCEL_TEST, new PlainEvent(mpe)); } public static void endTestMethod(Package pkg, int testIdentifier) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[test][test_identifier]", CollectUtility.toBody(testIdentifier)); submitEvent(pkg.getProject(), pkg, EventName.END_TEST, new PlainEvent(mpe)); } public static void assertTestMethod(Package pkg, int testIdentifier, int invocationIdentifier, String assertion, String param1, String param2) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[assert][test_identifier]", CollectUtility.toBody(testIdentifier)); mpe.addPart("event[assert][invoke_identifier]", CollectUtility.toBody(invocationIdentifier)); mpe.addPart("event[assert][assertion]", CollectUtility.toBody(assertion)); mpe.addPart("event[assert][param1]", CollectUtility.toBody(param1)); mpe.addPart("event[assert][param2]", CollectUtility.toBody(param2)); submitEvent(pkg.getProject(), pkg, EventName.ASSERTION, new PlainEvent(mpe)); } public static void objectBenchToFixture(Package pkg, File sourceFile, List<String> benchNames) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[source_file_name]", CollectUtility.toBodyLocal(new ProjectDetails(pkg.getProject()), sourceFile)); for (String name : benchNames) { mpe.addPart("event[bench_objects][][name]", CollectUtility.toBody(name)); } submitEvent(pkg.getProject(), pkg, EventName.BENCH_TO_FIXTURE, new PlainEvent(mpe)); } public static void fixtureToObjectBench(Package pkg, File sourceFile, List<NamedTyped> objects) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[source_file_name]", CollectUtility.toBodyLocal(new ProjectDetails(pkg.getProject()), sourceFile)); for (NamedTyped obj : objects) { mpe.addPart("event[bench_objects][][name]", CollectUtility.toBody(obj.getName())); mpe.addPart("event[bench_objects][][class_name]", CollectUtility.toBody(obj.getType())); } submitEvent(pkg.getProject(), pkg, EventName.FIXTURE_TO_BENCH, new PlainEvent(mpe)); } public static void testResult(Package pkg, DebuggerTestResult lastResult) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[class_name]", CollectUtility.toBody(lastResult.getQualifiedClassName())); mpe.addPart("event[method_name]", CollectUtility.toBody(lastResult.getMethodName())); mpe.addPart("event[run_time]", CollectUtility.toBody(lastResult.getRunTimeMs())); String status = "unknown"; if (lastResult.isSuccess()) status = "success"; else if (lastResult.isFailure()) status = "failure"; else if (lastResult.isError()) status = "error"; mpe.addPart("event[result]", CollectUtility.toBody(status)); if (!lastResult.isSuccess()) { mpe.addPart("event[exception_message]", CollectUtility.toBody(lastResult.getExceptionMessage())); mpe.addPart("event[exception_trace]", CollectUtility.toBody(lastResult.getTrace())); } submitEvent(pkg.getProject(), pkg, EventName.RUN_TEST, new PlainEvent(mpe)); }
| Adds the given stack trace to the MPE, using the given list name. | private static void addStackTrace(MultipartEntity mpe, String listName, SourceLocation[] stack) { for (int i = 0; i < stack.length; i++) { mpe.addPart(listName + "[][entry]", CollectUtility.toBody(i)); mpe.addPart(listName + "[][class_name]", CollectUtility.toBody(stack[i].getClassName())); mpe.addPart(listName + "[][class_source_name]", CollectUtility.toBody(stack[i].getFileName())); mpe.addPart(listName + "[][line_number]", CollectUtility.toBody(stack[i].getLineNumber())); } }
| Turns the Repository into an MPE with appropriate information | private static MultipartEntity getRepoMPE(Repository repo) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[vcs][vcs_type]", CollectUtility.toBody(repo.getVCSType())); mpe.addPart("event[vcs][protocol]", CollectUtility.toBody(repo.getVCSProtocol())); return mpe; } public static void showErrorIndicators(Package pkg, Collection<Integer> errorIdentifiers) { MultipartEntity mpe = new MultipartEntity(); if (errorIdentifiers.isEmpty()) { return; } for (Integer errorIdentifier : errorIdentifiers) { mpe.addPart("event[error_sequences][]", CollectUtility.toBody(errorIdentifier)); } submitEvent(pkg.getProject(), pkg, EventName.SHOWN_ERROR_INDICATOR, new PlainEvent(mpe)); } public static void showErrorMessage(Package pkg, int errorIdentifier, List<String> quickFixes) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[error_sequence]", CollectUtility.toBody(errorIdentifier)); if (quickFixes != null && !quickFixes.isEmpty()) { quickFixes.forEach(fix -> mpe.addPart("event[quick_fixes][][text]", CollectUtility.toBody(fix)) ); } submitEvent(pkg.getProject(), pkg, EventName.SHOWN_ERROR_MESSAGE, new PlainEvent(mpe)); } public static void fixExecuted(Package pkg, int errorIdentifier, int fixIndex) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[error_sequence]", CollectUtility.toBody(errorIdentifier)); mpe.addPart("event[fix_order]", CollectUtility.toBody(fixIndex)); submitEvent(pkg.getProject(), pkg, EventName.FIX_EXECUTED, new PlainEvent(mpe)); } public static void greenfootEvent(Project project, Package pkg, GreenfootInterfaceEvent greenfootEvent) { MultipartEntity mpe = new MultipartEntity(); EventName event = null; switch (greenfootEvent) { case WINDOW_ACTIVATED: event = EventName.GREENFOOT_WINDOW_ACTIVATED; break; case WORLD_RESET: event = EventName.GREENFOOT_WORLD_RESET; break; case WORLD_ACT: event = EventName.GREENFOOT_WORLD_ACT; break; case WORLD_RUN: event = EventName.GREENFOOT_WORLD_RUN; break; case WORLD_PAUSE: event = EventName.GREENFOOT_WORLD_PAUSE; break; } if (event != null) submitEvent(project, pkg, event, new PlainEvent(mpe)); } public static void codeCompletionStarted(Project project, Package pkg, Integer lineNumber, Integer columnNumber, String xpath, Integer subIndex, String stem, int codeCompletionId) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[code_completion][trigger]", CollectUtility.toBody("start")); mpe.addPart("event[code_completion][completion_sequence]", CollectUtility.toBody(codeCompletionId)); addCodeCompletionLocation(mpe, lineNumber, columnNumber, xpath, subIndex); if (stem != null) mpe.addPart("event[code_completion][stem]", CollectUtility.toBody(stem)); submitEvent(project, pkg, EventName.CODE_COMPLETION_STARTED, new PlainEvent(mpe)); } public static void codeCompletionEnded(Project project, Package pkg, Integer lineNumber, Integer columnNumber, String xpath, Integer subIndex, String stem, String replacement, int codeCompletionId) { MultipartEntity mpe = new MultipartEntity(); mpe.addPart("event[code_completion][trigger]", CollectUtility.toBody("selected")); mpe.addPart("event[code_completion][completion_sequence]", CollectUtility.toBody(codeCompletionId)); addCodeCompletionLocation(mpe, lineNumber, columnNumber, xpath, subIndex); if (stem != null) mpe.addPart("event[code_completion][stem]", CollectUtility.toBody(stem)); if (replacement != null) mpe.addPart("event[code_completion][replacement]", CollectUtility.toBody(replacement)); submitEvent(project, pkg, EventName.CODE_COMPLETION_ENDED, new PlainEvent(mpe)); } private static void addCodeCompletionLocation(MultipartEntity mpe, Integer lineNumber, Integer columnNumber, String xpath, Integer subIndex) { if (lineNumber != null) mpe.addPart("event[code_completion][line_number]", CollectUtility.toBody(lineNumber)); if (columnNumber != null) mpe.addPart("event[code_completion][column_number]", CollectUtility.toBody(columnNumber)); if (xpath != null) mpe.addPart("event[code_completion][xpath]", CollectUtility.toBody(xpath)); if (subIndex != null) mpe.addPart("event[code_completion][xml_index]", CollectUtility.toBody(subIndex)); } public static void unknownFrameCommandKey(Project project, Package pkg, String enclosingFrameXpath, int cursorIndex, char key) { MultipartEntity mpe = new MultipartEntity(); if (enclosingFrameXpath != null) { mpe.addPart("event[unknown_frame_command][enclosing_xpath]", CollectUtility.toBody(enclosingFrameXpath)); mpe.addPart("event[unknown_frame_command][enclosing_index]", CollectUtility.toBody(cursorIndex)); } mpe.addPart("event[unknown_frame_command][command]", CollectUtility.toBody(Character.toString(key))); submitEvent(project, pkg, EventName.UNKNOWN_FRAME_COMMAND, new PlainEvent(mpe)); }
| Records the Frame Catalogue's showing/hiding. | | @param project the current project | @param pkg the current package | @param enclosingFrameXpath the path for the frame that include the focused cursor, if any. | @param cursorIndex the focused cursor's index (if any) within the enclosing frame. | @param show true for showing and false for hiding | @param reason the user interaction which triggered the change. | public static void showHideFrameCatalogue(Project project, Package pkg, String enclosingFrameXpath, int cursorIndex, boolean show, FrameCatalogue.ShowReason reason) { MultipartEntity mpe = new MultipartEntity(); if (enclosingFrameXpath != null) { mpe.addPart("event[frame_catalogue_showing][enclosing_xpath]", CollectUtility.toBody(enclosingFrameXpath)); mpe.addPart("event[frame_catalogue_showing][enclosing_index]", CollectUtility.toBody(cursorIndex)); } mpe.addPart("event[frame_catalogue_showing][show]", CollectUtility.toBody(show)); mpe.addPart("event[frame_catalogue_showing][reason]", CollectUtility.toBody(reason.getText())); submitEvent(project, pkg, EventName.FRAME_CATALOGUE_SHOWING, new PlainEvent(mpe)); }
| Records a view mode change. | | @param project The current project | @param pkg The current package. May be <code>null</code>. | @param sourceFile The Stride file that its view mode has changed. | @param enclosingFrameXpath The path for the frame that include the focused cursor, if any. May be <code>null</code>. | @param cursorIndex The focused cursor's index (if any) within the enclosing frame. | @param oldView The old view mode that been switch from. | @param newView The new view mode that been switch to. | @param reason The user interaction which triggered the change. | public static void viewModeChange(Project project, Package pkg, File sourceFile, String enclosingFrameXpath, int cursorIndex, Frame.View oldView, Frame.View newView, Frame.ViewChangeReason reason) { MultipartEntity mpe = new MultipartEntity(); final ProjectDetails projDetails = new ProjectDetails(pkg.getProject()); mpe.addPart("event[view_mode_change][source_file_name]", CollectUtility.toBodyLocal(projDetails, sourceFile)); if (enclosingFrameXpath != null) { mpe.addPart("event[view_mode_change][enclosing_xpath]", CollectUtility.toBody(enclosingFrameXpath)); mpe.addPart("event[view_mode_change][enclosing_index]", CollectUtility.toBody(cursorIndex)); } mpe.addPart("event[view_mode_change][old_view]", CollectUtility.toBody(oldView.getText())); mpe.addPart("event[view_mode_change][new_view]", CollectUtility.toBody(newView.getText())); mpe.addPart("event[view_mode_change][reason]", CollectUtility.toBody(reason.getText())); submitEvent(project, pkg, EventName.VIEW_MODE_CHANGE, new PlainEvent(mpe)); } }
top, use, map, class DataCollectorImpl

.   submitEventNoData
.   submitEventWithLocalLocation
.   submitDebuggerEventWithLocation
.   submitEvent
.   success
.   makeData
.   compiled
.   bluejOpened
.   addExtensions
.   projectOpened
.   projectClosed
.   packageOpened
.   addCompleteFiles
.   success
.   makeData
.   addSourceHistoryItem
.   packageClosed
.   bluejClosed
.   restartVM

top, use, map, class EditedFileInfo

.   edit
.   isOneLineDiff
.   makeData
.   success
.   makeDiff
.   debuggerTerminate
.   debuggerChangeVisible
.   debuggerBreakpointToggle
.   debuggerContinue
.   debuggerHalt
.   debuggerStepInto
.   debuggerStepOver
.   debuggerHitBreakpoint
.   codePadSuccess
.   codePadError
.   codePadException
.   renamedClass
.   makeData
.   removeClass
.   makeData
.   conversion
.   makeData
.   addClass
.   openClass
.   closeClass
.   selectClass
.   classEvent
.   teamShareProject
.   teamPushProject
.   teamCommitProject
.   teamUpdateProject
.   teamStatusProject
.   teamHistoryProject
.   showHideTerminal
.   invokeCompileError
.   invokeMethodSuccess
.   invokeMethodException
.   invokeMethodTerminated
.   removeObject
.   inspectorClassShow
.   inspectorObjectShow
.   inspectorHide
.   benchGet
.   startTestMethod
.   cancelTestMethod
.   endTestMethod
.   assertTestMethod
.   objectBenchToFixture
.   fixtureToObjectBench
.   testResult
.   addStackTrace
.   getRepoMPE
.   showErrorIndicators
.   showErrorMessage
.   fixExecuted
.   greenfootEvent
.   codeCompletionStarted
.   codeCompletionEnded
.   addCodeCompletionLocation
.   unknownFrameCommandKey
.   showHideFrameCatalogue
.   viewModeChange




1502 neLoCode + 98 LoComm