package bluej.collect;

import bluej.Boot;
import bluej.Config;
import bluej.collect.DataCollectorImpl.EditedFileInfo;
import bluej.compiler.CompileInputFile;
import bluej.compiler.CompileReason;
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.editor.stride.FrameCatalogue;
import bluej.extmgr.ExtensionWrapper;
import bluej.groupwork.Repository;
import bluej.pkgmgr.Package;
import bluej.pkgmgr.Project;
import bluej.pkgmgr.target.ClassTarget;
import bluej.stride.generic.Frame;
import threadchecker.OnThread;
import threadchecker.Tag;

import java.io.File;
import java.util.*;


| DataCollector for sending off data. | | You can call these methods under any setting. It is this class's responsibility to check: | - That the user has actually opted in | - That we're not running Greenfoot | - That we don't keep attempting to send when there's no net connection (actually, DataSubmitter checks this for us) | | This class mainly acts as a proxy for the DataCollectorImpl class, which implements the actual | collection logic. | @OnThread(Tag.FXPlatform) public class DataCollector { private static final String PROPERTY_UUID = "blackbox.uuid"; private static final String PROPERTY_EXPERIMENT = "blackbox.experiment"; private static final String PROPERTY_PARTICIPANT = "blackbox.participant"; private static final String OPT_OUT = "optout";
| We decide at the very beginning of the session whether we are recording, based | on whether the user was opted in. Starting to record mid-session is fairly | useless, so even if the user opts in during the session, we won't record | their data until the next session begins. Thus, this variable | will never become true after startSession() has been called, althoug | it may become false if the user opts out mid-session | @OnThread(value = Tag.Any, requireSynchronized = true) private static boolean recordingThisSession;
| Session identifier. Never changes after startSession() has been called: | @OnThread(value = Tag.Any, requireSynchronized = true) private static String sessionUuid;
| These three variables can change during the execution: | @OnThread(value = Tag.Any, requireSynchronized = true) private static String uuid; @OnThread(value = Tag.Any, requireSynchronized = true) private static String experimentIdentifier; @OnThread(value = Tag.Any, requireSynchronized = true) private static String participantIdentifier;
| Keep track of which error (per-session compile error sequence ids) *messages* we have shown, | and thus already sent an event about. | private static final BitSet shownErrorMessages = new BitSet();
| Keep track of which error (per-session compile error sequence ids) *indicators* we have shown, | and thus already told the server about, either in a compiled event, or a shown_error_indicator event. | private static final BitSet shownErrorIndicators = new BitSet();
| Keep track of which errors (per-session compile error sequence ids) we have told the server | have been created. Due to threading back and forths, it is possible for us to be told that | error indicators have been shown before we've been told about the compile event that generated them. | private static final BitSet createdErrors = new BitSet();
| Checks whether we should send data. This takes into account whether we | are in Greenfoot, and opt-in status. It doesn't check whether we have stopped | sending due to connection problems -- DataSubmitter keeps track of that. | @OnThread(Tag.Any) private static synchronized boolean dontSend() { return (Config.isGreenfoot() && !Boot.isTrialRecording()) || !recordingThisSession; } @OnThread(Tag.FXPlatform) private static synchronized void startSession() { uuid = Config.getPropString(PROPERTY_UUID, null); if (!(OPT_OUT.equals(uuid)) && !uuidValidForRecording() ) { changeOptInOut(Boot.isTrialRecording()); } recordingThisSession = uuidValidForRecording(); if (recordingThisSession) { sessionUuid = UUID.randomUUID().toString(); } experimentIdentifier = Config.getPropString(PROPERTY_EXPERIMENT, null); participantIdentifier = Config.getPropString(PROPERTY_PARTICIPANT, null); }
| Checks if the user's UUID would be valid for recording, which is another way | of asking if the user has opted in. However, this is not the same as "are we currently |* sending data for this user", because if they opt-in mid-session, this method * will return true, but dontSend() will also return true (because recordingThisSession * will be false; it is set once at the very beginning of the session). @OnThread(Tag.Any) private static synchronized boolean uuidValidForRecording() { return uuid != null && !(OPT_OUT.equals(uuid)) && uuid.length() >= 32; }
| Show a dialog to ask the user for their opt-in/opt-out preference, | and then update the UUID accordingly | @OnThread(Tag.FXPlatform) public static synchronized void changeOptInOut(boolean forceOptIn) { boolean optedIn; if (forceOptIn) { optedIn = true; } else { DataCollectionDialog dlg = new DataCollectionDialog(); optedIn = dlg.showAndWait().orElse(false); } if (optedIn) { if (!uuidValidForRecording()) { uuid = UUID.randomUUID().toString(); } } else { uuid = OPT_OUT; recordingThisSession = false; } Config.putPropString(PROPERTY_UUID, uuid); }
| Gets the user's UUID | public static synchronized String getUserID() { return uuid; }
| Get the experiment identifier. | @OnThread(Tag.Any) public static synchronized String getExperimentIdentifier() { return experimentIdentifier; }
| Get the participant identifier. | @OnThread(Tag.Any) public static synchronized String getParticipantIdentifier() { return participantIdentifier; };
| Get the session identifier. | @OnThread(Tag.Any) public static synchronized String getSessionUuid() { return sessionUuid; }
| Gets a String to display to the user in the preferences, explaining their | current opt-in/recording status | @OnThread(Tag.Any) public static synchronized String getOptInOutStatus() { if (recordingThisSession) { return Config.getString("collect.status.optedin"); } else if (uuidValidForRecording()) { return Config.getString("collect.status.nextsession"); } else { return Config.getString("collect.status.optedout"); } } public static synchronized void setExperimentIdentifier(String experimentIdentifier) { DataCollector.experimentIdentifier = experimentIdentifier; Config.putPropString(PROPERTY_EXPERIMENT, experimentIdentifier); } public static synchronized void setParticipantIdentifier(String participantIdentifier) { DataCollector.participantIdentifier = participantIdentifier; Config.putPropString(PROPERTY_PARTICIPANT, participantIdentifier); } @OnThread(Tag.FXPlatform) public static void bluejOpened(String osVersion, String javaVersion, String bluejVersion, String interfaceLanguage, List<ExtensionWrapper> extensions) { if (Config.isGreenfoot() && !Boot.isTrialRecording()) return; startSession(); if (dontSend()) return; DataCollectorImpl.bluejOpened(osVersion, javaVersion, bluejVersion, interfaceLanguage, extensions); } public static void bluejClosed() { if (dontSend()) return; DataCollectorImpl.bluejClosed(); }
| Record a compile event. This may be a javac compile, but it may also be a Stride early or late error check. | | @param proj The project involved in the compilation | @param pkg The package involved in the compilation. Due to BlueJ's design, we only ever compile one package at a time. | May be null if it cannot be determined for some reason | @param sources The collection of files fed to the compilation as input. | @param diagnostics The diagnostics (i.e. errors and warnings) which were generated | as a result of the compile. May be empty, especially if compile was successful. | @param success Was the compile a success? | @param reason The reason for performing the compilation. This is recorded on the server | @param compilationSequence A sequence identifier (unique within this session) of the original trigger of the compilation event. | If we compile Stride, we may get an early, normal and late compilation result passed to three | separate calls of this method, but they will all have the same compilationSequence | value, to allow them to be reassembled by a later researcher. The special value -1 | means it is non-applicable or unknown, and in this case will not be sent to the server. | public static void compiled(Project proj, Package pkg, CompileInputFile[] sources, List<DiagnosticWithShown> diagnostics, boolean success, CompileReason reason, int compilationSequence) { if (dontSend()) return; Set<Integer> pendingShow = new HashSet<>(); for (DiagnosticWithShown dws : diagnostics) { if (shownErrorIndicators.get(dws.getDiagnostic().getIdentifier()) || dws.wasShownToUser()) { pendingShow.add(dws.getDiagnostic().getIdentifier()); shownErrorIndicators.set(dws.getDiagnostic().getIdentifier()); } createdErrors.set(dws.getDiagnostic().getIdentifier()); } DataCollectorImpl.compiled(proj, pkg, sources, diagnostics, success, reason, compilationSequence); if (!pendingShow.isEmpty()) { DataCollectorImpl.showErrorIndicators(pkg, pendingShow); } } public static void debuggerTerminate(Project project) { if (dontSend()) return; DataCollectorImpl.debuggerTerminate(project); } public static void debuggerChangeVisible(Project project, boolean newVis) { if (dontSend()) return; DataCollectorImpl.debuggerChangeVisible(project, newVis); } public static void debuggerContinue(Project project, String threadName) { if (dontSend()) return; DataCollectorImpl.debuggerContinue(project, threadName); } public static void debuggerHalt(Project project, String threadName, SourceLocation[] stack) { if (dontSend()) return; DataCollectorImpl.debuggerHalt(project, threadName, stack); } public static void debuggerStepInto(Project project, String threadName, SourceLocation[] stack) { if (dontSend()) return; DataCollectorImpl.debuggerStepInto(project, threadName, stack); } public static void debuggerStepOver(Project project, String threadName, SourceLocation[] stack) { if (dontSend()) return; DataCollectorImpl.debuggerStepOver(project, threadName, stack); } public static void debuggerHitBreakpoint(Project project, String threadName, SourceLocation[] stack) { if (dontSend()) return; DataCollectorImpl.debuggerHitBreakpoint(project, threadName, stack); } public static void invokeCompileError(Package pkg, String code, String compilationError) { if (dontSend()) return; DataCollectorImpl.invokeCompileError(pkg, code, compilationError); } public static void invokeMethodSuccess(Package pkg, String code, String objName, String typeName, int testIdentifier, int invocationIdentifier) { if (dontSend()) return; DataCollectorImpl.invokeMethodSuccess(pkg, code, objName, typeName, testIdentifier, invocationIdentifier); } public static void invokeMethodException(Package pkg, String code, ExceptionDescription ed) { if (dontSend()) return; DataCollectorImpl.invokeMethodException(pkg, code, ed); } public static void invokeMethodTerminated(Package pkg, String code) { if (dontSend()) return; DataCollectorImpl.invokeMethodTerminated(pkg, code); } public static void assertTestMethod(Package pkg, int testIdentifier, int invocationIdentifier, String assertion, String param1, String param2) { if (dontSend()) return; DataCollectorImpl.assertTestMethod(pkg, testIdentifier, invocationIdentifier, assertion, param1, param2); } public static void removeObject(Package pkg, String name) { if (dontSend()) return; DataCollectorImpl.removeObject(pkg, name); } public static void codePadSuccess(Package pkg, String command, String output) { if (dontSend()) return; DataCollectorImpl.codePadSuccess(pkg, command, output); } public static void codePadError(Package pkg, String command, String error) { if (dontSend()) return; DataCollectorImpl.codePadError(pkg, command, error); } public static void codePadException(Package pkg, String command, String exception) { if (dontSend()) return; DataCollectorImpl.codePadException(pkg, command, exception); } public static void teamCommitProject(Project project, Repository repo, Set<File> committedFiles) { if (dontSend()) return; DataCollectorImpl.teamCommitProject(project, repo, committedFiles); }
| 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, Set<File> pushedFiles) { if (dontSend()) return; DataCollectorImpl.teamPushProject(project, repo, pushedFiles); } public static void teamShareProject(Project project, Repository repo) { if (dontSend()) return; DataCollectorImpl.teamShareProject(project, repo); } public static void addClass(Package pkg, ClassTarget ct) { if (dontSend()) return; DataCollectorImpl.addClass(pkg, ct); } public static void teamUpdateProject(Project project, Repository repo, Set<File> updatedFiles) { if (dontSend()) return; DataCollectorImpl.teamUpdateProject(project, repo, updatedFiles); } public static void teamHistoryProject(Project project, Repository repo) { if (dontSend()) return; DataCollectorImpl.teamHistoryProject(project, repo); } public static void teamStatusProject(Project project, Repository repo, Map<File, String> status) { if (dontSend()) return; DataCollectorImpl.teamStatusProject(project, repo, status); } public static void debuggerBreakpointToggle(Package pkg, File sourceFile, int lineNumber, boolean newState) { if (dontSend()) return; DataCollectorImpl.debuggerBreakpointToggle(pkg, sourceFile, lineNumber, newState); }
| 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, File oldFrameSourceFile, File newFrameSourceFile, File oldJavaSourceFile, File newJavaSourceFile) { if (dontSend()) { return; } DataCollectorImpl.renamedClass(pkg, oldFrameSourceFile, newFrameSourceFile, oldJavaSourceFile, newJavaSourceFile); }
| Records removing class files. | | @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) { if (dontSend()) { return; } DataCollectorImpl.removeClass(pkg, frameSourceFile, javaSourceFile); } public static void openClass(Package pkg, File sourceFile) { if (dontSend()) return; DataCollectorImpl.openClass(pkg, sourceFile); } public static void closeClass(Package pkg, File sourceFile) { if (dontSend()) return; DataCollectorImpl.closeClass(pkg, sourceFile); } public static void selectClass(Package pkg, File sourceFile) { if (dontSend()) return; DataCollectorImpl.selectClass(pkg, sourceFile); }
| Record conversion from Stride to Java. | @param pkg The package in which the files live. | @param oldSourceFile The old Stride source file (which will be deleted by the caller) | @param newSourceFile The Java source file (which will be retained by the caller) | public static void convertStrideToJava(Package pkg, File oldSourceFile, File newSourceFile) { if (dontSend()) return; DataCollectorImpl.conversion(pkg, newSourceFile, oldSourceFile, true); }
| Record conversion from Java to Stride. | @param pkg The package in which the files live. | @param oldSourceFile The old Java source file (which will now be generated, rather than edited directly) | @param newSourceFile The new Stride source file (which will now be edited and used to generate the Java) | public static void convertJavaToStride(Package pkg, File oldSourceFile, File newSourceFile) { if (dontSend()) return; DataCollectorImpl.conversion(pkg, oldSourceFile, newSourceFile, false); } public static void editJava(Package pkg, File path, String source, boolean includeOneLineEdits) { if (dontSend()) return; DataCollectorImpl.edit(pkg, Collections.singletonList(new EditedFileInfo("diff", path, source, includeOneLineEdits, null, null))); } public static void editStride(Package pkg, File javaPath, String javaSource, File stridePath, String strideSource, StrideEditReason reason) { if (dontSend()) return; DataCollectorImpl.edit(pkg, Arrays.asList( new EditedFileInfo("diff_generated", javaPath, javaSource, true, stridePath, null), new EditedFileInfo("diff", stridePath, strideSource, true, null, reason) )); } public static void packageOpened(Package pkg) { if (dontSend()) return; DataCollectorImpl.packageOpened(pkg); } public static void packageClosed(Package pkg) { if (dontSend()) return; DataCollectorImpl.packageClosed(pkg); } public static void benchGet(Package pkg, String benchName, String typeName, int testIdentifier) { if (dontSend()) return; DataCollectorImpl.benchGet(pkg, benchName, typeName, testIdentifier); } public static void endTestMethod(Package pkg, int testIdentifier) { if (dontSend()) return; DataCollectorImpl.endTestMethod(pkg, testIdentifier); } public static void cancelTestMethod(Package pkg, int testIdentifier) { if (dontSend()) return; DataCollectorImpl.cancelTestMethod(pkg, testIdentifier); } public static void startTestMethod(Package pkg, int testIdentifier, File sourceFile, String testName) { if (dontSend()) return; DataCollectorImpl.startTestMethod(pkg, testIdentifier, sourceFile, testName); } public static void restartVM(Project project) { if (dontSend()) return; DataCollectorImpl.restartVM(project); } public static void testResult(Package pkg, DebuggerTestResult lastResult) { if (dontSend()) return; DataCollectorImpl.testResult(pkg, lastResult); } public static void projectOpened(Project proj, List<ExtensionWrapper> projectExtensions) { if (dontSend()) return; DataCollectorImpl.projectOpened(proj, projectExtensions); } public static void projectClosed(Project proj) { if (dontSend()) return; DataCollectorImpl.projectClosed(proj); } public static void inspectorObjectShow(Package pkg, ObjectInspector inspector, String benchName, String className, String displayName) { if (dontSend()) return; DataCollectorImpl.inspectorObjectShow(pkg, inspector, benchName, className, displayName); } public static void inspectorHide(Project project, Inspector inspector) { if (dontSend()) return; DataCollectorImpl.inspectorHide(project, inspector); }
| 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, ClassInspector inspector, String className) { if (dontSend()) return; DataCollectorImpl.inspectorClassShow(proj, pkg, inspector, className); } public static void showErrorIndicators(Package pkg, Collection<Integer> errorIdentifiers) { if (dontSend()) return; HashSet<Integer> previouslyUnshown = new HashSet<>(errorIdentifiers); previouslyUnshown.removeAll(asCollection(shownErrorIndicators)); if (previouslyUnshown.isEmpty()) { return; } else { HashSet<Integer> toSend = new HashSet<>(previouslyUnshown); toSend.retainAll(asCollection(createdErrors)); if (!toSend.isEmpty()) { DataCollectorImpl.showErrorIndicators(pkg, toSend); } for (Integer id : previouslyUnshown) { shownErrorIndicators.set(id); } } }
| Mirrors a BitSet as a collection of integers (the indexes of the set bits). | Surprisingly, there's no method in BitSet itself to support this. | | Rather than make a copy of the collection, we create a fake collection | which is based on the original bitset. Thus, if the original bitset changes, | the returned collection will also change. Hence why it's named asCollection | rather than toCollection. | private static Collection asCollection(BitSet bitSet) { return new AbstractCollection<Integer>() { @Override public Iterator iterator() { return new Iterator<Integer>() { int nextBit = bitSet.nextSetBit(0); @Override public boolean hasNext() { return nextBit != -1; } @Override public Integer next() { int retBit = nextBit; nextBit = bitSet.nextSetBit(nextBit + 1); return retBit; } }; } @Override public boolean contains(Object o) { if (o instanceof Integer) return bitSet.get((Integer)o); else{ return false; } } @Override public int size() { return bitSet.cardinality(); } }; } public static void showErrorMessage(Package pkg, int errorIdentifier, List<String> quickFixes) { if (dontSend()) return; if (shownErrorMessages.get(errorIdentifier)) return; shownErrorMessages.set(errorIdentifier); DataCollectorImpl.showErrorMessage(pkg, errorIdentifier, quickFixes); } public static void fixExecuted(Package aPackage, int errorIdentifier, int fixIndex) { if (dontSend()) return; DataCollectorImpl.fixExecuted(aPackage, errorIdentifier, fixIndex); } public static void recordGreenfootEvent(Project project, GreenfootInterfaceEvent event) { if (dontSend()) return; DataCollectorImpl.greenfootEvent(project, project.getPackage(""), event); }
| Record that code completion has been triggered. | | @param ct The class target in which code completion was triggered. | @param lineNumber The Java line number, or null if Stride is being used | @param columnNumber The Java column number, or null if Stride is being used | @param xpath The XPath to the Stride element, or null if Java is being used | @param subIndex The sub-index within the Stride element, or null if Java is being used | @param stem The initial String stem used to decide initially eligible items | @param codeCompletionId The ID of the code completion, unique to this session. Used to match with later ending event. | public static void codeCompletionStarted(ClassTarget ct, Integer lineNumber, Integer columnNumber, String xpath, Integer subIndex, String stem, int codeCompletionId) { if (dontSend()) return; DataCollectorImpl.codeCompletionStarted(ct.getPackage().getProject(), ct.getPackage(), lineNumber, columnNumber, xpath, subIndex, stem, codeCompletionId); }
| Record that code completion has ended. | | @param ct The class target in which code completion was ended. | @param lineNumber The Java line number, or null if Stride is being used | @param columnNumber The Java column number, or null if Stride is being used | @param xpath The XPath to the Stride element, or null if Java is being used | @param subIndex The sub-index within the Stride element, or null if Java is being used | @param stem The current String stem at the point where the code completion was ended. | @param replacement The replacement which was chosen from the code completion list. | @param codeCompletionId The ID of the code completion, unique to this session. Used to match with later ending event. | public static void codeCompletionEnded(ClassTarget ct, Integer lineNumber, Integer columnNumber, String xpath, Integer subIndex, String stem, String replacement, int codeCompletionId) { if (dontSend()) return; DataCollectorImpl.codeCompletionEnded(ct.getPackage().getProject(), ct.getPackage(), lineNumber, columnNumber, xpath, subIndex, stem, replacement, codeCompletionId); } public static void unknownFrameCommandKey(ClassTarget ct, String enclosingFrameXpath, int cursorIndex, char key) { if (dontSend()) return; DataCollectorImpl.unknownFrameCommandKey(ct.getPackage().getProject(), ct.getPackage(), enclosingFrameXpath, cursorIndex, key); }
| Records the Frame Catalogue's showing/hiding. | | @param project The current project | @param pkg The current package. May be <code>null</code>. | @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 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) { if (dontSend()) { return; } DataCollectorImpl.showHideFrameCatalogue(project, pkg, enclosingFrameXpath, cursorIndex, show, reason); }
| Records a view mode change. | | @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(Package pkg, File sourceFile, String enclosingFrameXpath, int cursorIndex, Frame.View oldView, Frame.View newView, Frame.ViewChangeReason reason) { if (dontSend()) { return; } DataCollectorImpl.viewModeChange(pkg.getProject(), pkg, sourceFile, enclosingFrameXpath, cursorIndex, oldView, newView, reason); } public static boolean hasGivenUp() { return DataSubmitter.hasGivenUp(); } public static class NamedTyped { private String name; private String type; public NamedTyped(String name, String type) { this.name = name; this.type = type; } public String getName() { return name; } public String getType() { return type; } } public static void fixtureToObjectBench(Package pkg, File sourceFile, List<NamedTyped> objects) { if (dontSend()) return; DataCollectorImpl.fixtureToObjectBench(pkg, sourceFile, objects); } public static void objectBenchToFixture(Package pkg, File sourceFile, List<String> benchNames) { if (dontSend()) return; DataCollectorImpl.objectBenchToFixture(pkg, sourceFile, benchNames); } public static void showHideTerminal(Project project, boolean show) { if (dontSend()) return; DataCollectorImpl.showHideTerminal(project, show); } }
top, use, map, class DataCollector

.   dontSend
.   startSession
.   uuidValidForRecording
.   changeOptInOut
.   getUserID
.   getExperimentIdentifier
.   getParticipantIdentifier
.   getSessionUuid
.   getOptInOutStatus
.   setExperimentIdentifier
.   setParticipantIdentifier
.   bluejOpened
.   bluejClosed
.   compiled
.   debuggerTerminate
.   debuggerChangeVisible
.   debuggerContinue
.   debuggerHalt
.   debuggerStepInto
.   debuggerStepOver
.   debuggerHitBreakpoint
.   invokeCompileError
.   invokeMethodSuccess
.   invokeMethodException
.   invokeMethodTerminated
.   assertTestMethod
.   removeObject
.   codePadSuccess
.   codePadError
.   codePadException
.   teamCommitProject
.   teamPushProject
.   teamShareProject
.   addClass
.   teamUpdateProject
.   teamHistoryProject
.   teamStatusProject
.   debuggerBreakpointToggle
.   renamedClass
.   removeClass
.   openClass
.   closeClass
.   selectClass
.   convertStrideToJava
.   convertJavaToStride
.   editJava
.   editStride
.   packageOpened
.   packageClosed
.   benchGet
.   endTestMethod
.   cancelTestMethod
.   startTestMethod
.   restartVM
.   testResult
.   projectOpened
.   projectClosed
.   inspectorObjectShow
.   inspectorHide
.   inspectorClassShow
.   showErrorIndicators
.   asCollection
.   iterator
.   hasNext
.   next
.   contains
.   size
.   showErrorMessage
.   fixExecuted
.   recordGreenfootEvent
.   codeCompletionStarted
.   codeCompletionEnded
.   unknownFrameCommandKey
.   showHideFrameCatalogue
.   viewModeChange
.   hasGivenUp

top, use, map, class DataCollector . NamedTyped

.   NamedTyped
.   getName
.   getType
.   fixtureToObjectBench
.   objectBenchToFixture
.   showHideTerminal




889 neLoCode + 115 LoComm