diff --git a/src/edu/csus/ecs/pc2/core/IInternalController.java b/src/edu/csus/ecs/pc2/core/IInternalController.java index d77e89c93..2fad83a31 100644 --- a/src/edu/csus/ecs/pc2/core/IInternalController.java +++ b/src/edu/csus/ecs/pc2/core/IInternalController.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2020 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core; import java.io.IOException; @@ -39,23 +39,23 @@ /** * Represents functions provided by modules comprising the contest engine. - * + * * Provides the methods to start PC2 clients and servers. *

- * An example of starting a server: + * An example of starting a server: *

* * public static void main(String[] args) {
- *

+ *
* IInternalContest contest = new InternalContest();
* IInternalController controller = new InternalController (contest);
* String serverArgs = "--server"; controller.start(serverArgs);
- *
- * } + *
+ * } * *

- * - * To start a client: + * + * To start a client: *

* * public static void main(String[] args) {
@@ -64,10 +64,10 @@ * IInternalController controller = new InternalController (contest);
* controller.start(args);
* - * } + * } *
*

- * + * * @see edu.csus.ecs.pc2.Starter * @author pc2@ecs.csus.edu * @version $Id$ @@ -78,50 +78,99 @@ public interface IInternalController { /** * Submit a Judge Run to the server. - * + * * @param problem the {@link Problem} for which the Submission applies * @param language the {@link Language} used in the Submission * @param mainFileName the name of the file containing the main program source code * @param otherFiles an array of {@link SerializedFile}s containing additional source files being submitted with the Run - * + * * @throws Exception if an error occurs while attempting to send the Run to the Server */ void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles) throws Exception; /** * Submit a Judge Run to the server. - * + * * @param problem the {@link Problem} for which the Submission applies * @param language the {@link Language} used in the Submission * @param mainFileName the name of the file containing the main program source code * @param otherFiles an array of {@link SerializedFile}s containing additional source files being submitted with the Run - * @param overrideSubmissionTimeMS a value which, if non-zero, is to be used as the submission time of the Judge Run; + * @param overrideStopOnFailure if true, then do not stop judging on first failure. if false, use the setting for the problem + * + * @throws Exception if an error occurs while attempting to send the Run to the Server + */ + void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, boolean overrideStopOnFailure) throws Exception; + + /** + * Submit a Judge Run to the server. + * + * @param problem the {@link Problem} for which the Submission applies + * @param language the {@link Language} used in the Submission + * @param mainFileName the name of the file containing the main program source code + * @param otherFiles an array of {@link SerializedFile}s containing additional source files being submitted with the Run + * @param overrideSubmissionTimeMS a value which, if non-zero, is to be used as the submission time of the Judge Run; * only has effect when Contest Information "CCS Test Mode" is true * @param overrideRunId a value which, if non-zero, is to be used as the RunId for the submission instead of any * internally-assigned RunId; only has effect when Contest Information "CCS Test Mode" is true - * + * * @throws Exception if an error occurs while attempting to send the Run to the Server */ - void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, + void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, long overrideSubmissionTimeMS, long overrideRunId) throws Exception; /** * Submit a Judge Run to the server. - * + * + * @param problem the {@link Problem} for which the Submission applies + * @param language the {@link Language} used in the Submission + * @param mainFileName the name of the file containing the main program source code + * @param otherFiles an array of {@link SerializedFile}s containing additional source files being submitted with the Run + * @param overrideSubmissionTimeMS a value which, if non-zero, is to be used as the submission time of the Judge Run; + * only has effect when Contest Information "CCS Test Mode" is true + * @param overrideRunId a value which, if non-zero, is to be used as the RunId for the submission instead of any + * internally-assigned RunId; only has effect when Contest Information "CCS Test Mode" is true + * @param overrideStopOnFailure if true, then do not stop judging on first failure. if false, use the setting for the problem + * + * @throws Exception if an error occurs while attempting to send the Run to the Server + */ + void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, + long overrideSubmissionTimeMS, long overrideRunId, boolean overrideStopOnFailure) throws Exception; + + /** + * Submit a Judge Run to the server. + * * @param problem the {@link Problem} for which the Submission applies * @param language the {@link Language} used in the Submission * @param mainFile a {@link SerializedFile} containing the main program source code * @param otherFiles an array of {@link SerializedFile}s containing additional source files being submitted with the Run - * @param overrideSubmissionTimeMS a value which, if non-zero, is to be used as the submission time of the Judge Run; + * @param overrideSubmissionTimeMS a value which, if non-zero, is to be used as the submission time of the Judge Run; * only has effect when Contest Information "CCS Test Mode" is true * @param overrideRunId a value which, if non-zero, is to be used as the RunId for the submission instead of any * internally-assigned RunId; only has effect when Contest Information "CCS Test Mode" is true - * + * * @throws Exception if an error occurs while attempting to send the Run to the Server */ - void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, + void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, long overrideSubmissionTimeMS, long overrideRunId) throws Exception; + /** + * Submit a Judge Run to the server. + * + * @param problem the {@link Problem} for which the Submission applies + * @param language the {@link Language} used in the Submission + * @param mainFile a {@link SerializedFile} containing the main program source code + * @param otherFiles an array of {@link SerializedFile}s containing additional source files being submitted with the Run + * @param overrideSubmissionTimeMS a value which, if non-zero, is to be used as the submission time of the Judge Run; + * only has effect when Contest Information "CCS Test Mode" is true + * @param overrideRunId a value which, if non-zero, is to be used as the RunId for the submission instead of any + * internally-assigned RunId; only has effect when Contest Information "CCS Test Mode" is true + * @param overrideStopOnFailure if true, then do not stop judging on first failure. if false, use the setting for the problem + * + * @throws Exception if an error occurs while attempting to send the Run to the Server + */ + void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, + long overrideSubmissionTimeMS, long overrideRunId, boolean overrideStopOnFailure) throws Exception; + void setSiteNumber(int i); @@ -129,21 +178,21 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Send to client (or server), if necessary forward to another server. - * + * * @param packet */ void sendToClient(Packet packet); /** * Send to all logged in servers. - * + * * @param packet */ void sendToServers(Packet packet); /** * Send to a remote server. - * + * * @param siteNumber * @param packet */ @@ -151,49 +200,49 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Send to all judges on local site. - * + * * @param packet */ void sendToJudges(Packet packet); /** * Send to all administrators on local site. - * + * * @param packet */ void sendToAdministrators(Packet packet); /** * Send to all scoreboard on local site. - * + * * @param packet */ void sendToScoreboards(Packet packet); /** * Send to all teams on local site. - * + * * @param packet */ void sendToTeams(Packet packet); /** * Send to all spectator/API clients - * + * * @param packet */ void sendToSpectators(Packet packet); /** * Start InternalController with command line arguments. - * + * * @param stringArray */ void start(String[] stringArray); /** * Login to server, start MainUI. - * + * * @param loginName * @param password */ @@ -201,7 +250,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Login to server, wait for login - * + * * @param loginName * @param password * @return @@ -211,23 +260,23 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Logoff a client. - * + * * Logs this client off, or sends request to log client off. - * + * * @param clientId */ void logoffUser(ClientId clientId); /** * Start the UI for the input client. - * + * * @param clientId */ void startMainUI(ClientId clientId); /** * Request a run from the server. - * + * * @param run * - the run to retrieve * @param readOnly @@ -239,14 +288,14 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Request to checkout a judged run, to rejudge the run. - * + * * @param theRun */ void checkOutRejudgeRun(Run theRun); /** * Submit judgement from run to judge. - * + * * @param run * @param judgementRecord */ @@ -254,7 +303,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Cancel selected run. - * + * * @param run */ void cancelRun(Run run); @@ -262,21 +311,21 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, void addNewSite(Site site); void addNewProblem(Problem problem, ProblemDataFiles problemDataFiles); - + void addNewProblem(Problem [] problem, ProblemDataFiles [] problemDataFiles); void addProblem(Problem problem); /** * Add a new Judgement. - * + * * @param judgement */ void addNewJudgement(Judgement judgement); /** * Replace judgement list with new judgement list. - * + * * @param judgementList */ void setJudgementList(Judgement[] judgementList); @@ -286,13 +335,13 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, void updateRun(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles); void sendServerLoginRequest(int inSiteNumber) throws Exception; - + /** - * Is this controller using GUI. + * Is this controller using GUI. * @return true if using GUI, false if using text only */ boolean isUsingGUI(); - + /** * Is this controller suppressing display of Connections grids. * @return true if GUIs using this controller should suppress display of Connections grids. @@ -314,10 +363,10 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, void updateProblem(Problem problem); void updateProblem(Problem problem, ProblemDataFiles problemDataFiles); - + /** * Send packet to local server to switch profile. - * + * * @param currentProfile profile to switch from * @param switchToProfile profile to switch to */ @@ -325,7 +374,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Clone profile (and potentially switch to the new profile). - * + * * @param profile current profile * @param settings set of changes to clone * @param switchNow true means switch to new profile now @@ -336,14 +385,14 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Get contest log. - * + * * @return */ Log getLog(); /** * Send message to server that needs attention/resolution. - * + * * @param event * optional event * @param message @@ -355,7 +404,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Generate new accounts on a server. - * + * * @param clientTypeName * @param siteNumber * site number to generate accounts. @@ -367,7 +416,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Generate new accounts for current site. - * + * * @param clientTypeName * @param count * @param startNumber @@ -377,7 +426,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Submit a clarification. - * + * * @param problem * @param question */ @@ -385,7 +434,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Request clarification to answer. - * + * * @param clarification * @param readOnly */ @@ -393,23 +442,23 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Cancel requested clarification. - * + * * @param clarification */ void cancelClarification(Clarification clarification); /** * Answer a clarification. - * + * * @param clarification */ void submitClarificationAnswer(Clarification clarification); /** * Force connection off. - * + * * Remove local connection, or send to server to remove connection. - * + * * @param connectionHandlerID */ void forceConnectionDrop(ConnectionHandlerID connectionHandlerID); @@ -440,13 +489,13 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, void addNewLanguage(Language language); void addNewLanguages(Language[] languages); - + void updateLanguage(Language language); void updateLanguages(Language[] languages); void addNewGroup(Group group); - + void addNewGroups(Group[] groups); void updateGroup(Group group); @@ -467,14 +516,14 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Load contest settings from disk and initialize InternalContest. - * @param contest - * + * @param contest + * * @throws FileSecurityException * @throws ClassNotFoundException * @throws IOException */ void initializeServer(IInternalContest contest) throws IOException, ClassNotFoundException, FileSecurityException; - + void initializeStorage (IStorage storage); void addNewClientSettings(ClientSettings newClientSettings); @@ -483,7 +532,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Get Security Level. - * + * * @return current security level */ int getSecurityLevel(); @@ -492,38 +541,38 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Send packet to server. - * + * * Also can be used to send a packet from this * server to this server as a consistent interface * to the server (esp from classes like ServerView) - * + * * @param packet */ void sendToLocalServer(Packet packet); /** * Get name of host contacted. - * + * * On server, gets name of host where listener listens. - * + * * @return name of host server */ String getHostContacted(); /** * Get port number of host contacted. - * + * * On server, gets port where listening. - * + * * @return */ int getPortContacted(); /** * Gets a run from the server. - * + * * Does not checkout run, simply requests the run info. Security note: only a non-team client can request runs. - * + * * @param run * @throws FileSecurityException * @throws ClassNotFoundException @@ -533,21 +582,21 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Send a compiling message to the Server. - * + * * @param run */ void sendCompilingMessage(Run run); /** * Send a executing message to the Server. - * + * * @param run */ void sendExecutingMessage(Run run); /** * Send a validating message to the Server. - * + * * @param run */ void sendValidatingMessage(Run run); @@ -558,21 +607,21 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Reset contest. - * + * * @param clientResettingContest */ void resetContest(ClientId clientResettingContest, boolean eraseProblems, boolean eraseLanguages); /** * Update existing judgement. - * + * * @param newJudgement */ void updateJudgement(Judgement newJudgement); /** * Update current Profile information. - * + * * @param profile */ void updateProfile(Profile profile); @@ -581,21 +630,21 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Register a plugin. - * + * * @param plugin */ void register(UIPlugin plugin); - + /** * Get list of plugins. - * + * * @return */ UIPlugin[] getPluginList(); /** * Update/replace contest and controller for all registered UI Plugins. - * + * * @param inContest * @param inController */ @@ -604,16 +653,16 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, void addPacketListener(IPacketListener packetListener); void removePacketListener(IPacketListener packetListener); - + void incomingPacket (Packet packet); - + void outgoingPacket (Packet packet); - + /** * Start Log Window. - * + * * Only starts if {@link #isUsingGUI()} returns true; - * + * * @param contest */ ILogWindow startLogWindow(IInternalContest contest); @@ -623,7 +672,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, * @param showWindow set LogWindow visible. */ void showLogWindow(boolean showWindow); - + boolean isLogWindowVisible(); /** @@ -635,10 +684,10 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Send Sync Submissions packet. - * + * * Send a packet to tell all servers to sync up their * submission and other local data with all servers. - * + * * @param profile */ void syncProfileSubmissions(Profile profile); @@ -650,30 +699,30 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Send shutdown server packet. - * + * * @param siteNumber site number to shut down. */ void sendShutdownSite(int siteNumber); /** * Shutdown this server. - * + * * @param requestor */ void shutdownServer(ClientId requestor); /** * Shutdown all remote servers. - * + * * Sends packet to all servers to shutdown. - * + * * @param requestor */ void shutdownRemoteServers(ClientId requestor); /** * Shutdown remove server (Server). - * + * * @param requestor * @param siteNumber */ @@ -681,17 +730,17 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Update Finalize data - * @param data + * @param data */ void updateFinalizeData(FinalizeData data); - + /** * Using GUI?. - * + * * @param usingGUI true means show GUI message, false means do not show GUI messages. */ void setUsingGUI(boolean usingGUI); - + void updateCategories(Category[] categories); void updateCategory(Category newCategory); @@ -699,7 +748,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, void addNewCategory(Category newCategory); void startPlayback(PlaybackInfo playbackInfo); - + /** * Send submitted run to Run Submission Interface. * @param run @@ -714,10 +763,10 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Send to all logged in Judges, Admins, Boards and optionally to other sites. - * - * This sends all sorts of packets to all logged in clients (except teams). + * + * This sends all sorts of packets to all logged in clients (except teams). * Typically sendToServers is set if this is the originating site, if not done then a nasty circular path will occur. - * + * * @param packet * @param sendToServers if true then send to other server. */ @@ -725,16 +774,16 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Override the connection manager. - * + * * @see ITransportManager * @param connectionManager */ void setConnectionManager(ITransportManager connectionManager); - + /** * Creates an {@link AutoStarter} if none exists, and then instructs the AutoStarter to update its Scheduled Start Task to correspond to the Scheduled Start Time information in the * {@link ContestInformation} object in the received {@link IInternalContest}. - * + * * @param aContest * - the Contest (Model) containing the Scheduled Start Time information * @param aController @@ -750,7 +799,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Submit a run to the server for a different client. - * + * * @param submitter - override submitter, if used the logged in client must have Permission.Type.SHADOW_PROXY_TEAM selected. * @param problem * @param language @@ -763,7 +812,7 @@ void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, /** * Submit a run to the server for a different client with entry_point - * + * * @param submitter - override submitter, if used the logged in client must have Permission.Type.SHADOW_PROXY_TEAM selected. * @param problem * @param language diff --git a/src/edu/csus/ecs/pc2/core/InternalController.java b/src/edu/csus/ecs/pc2/core/InternalController.java index 41ff0d853..c4b46c823 100644 --- a/src/edu/csus/ecs/pc2/core/InternalController.java +++ b/src/edu/csus/ecs/pc2/core/InternalController.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2022 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core; import java.io.File; @@ -95,7 +95,7 @@ /** * Implementation of InternalContest InternalController. - * + * * Run Flow, submit run. *

    *
  1. Team: {@link #submitRun(Problem, Language, String)} @@ -128,12 +128,12 @@ * {@link edu.csus.ecs.pc2.core.model.RunEvent.Action#RUN_AVAILABLE} *
*

- * + * * @author pc2@ecs.csus.edu */ public class InternalController implements IInternalController, ITwoToOne, IBtoA { - + private boolean haltOnFatalError = true; /** @@ -161,27 +161,27 @@ public class InternalController implements IInternalController, ITwoToOne, IBtoA private Log log; private Ini ini = new Ini(); - + private boolean suppressConnectionsPaneDisplay = false; private boolean suppressLoginsPaneDisplay = false; /** * The port that the server will listen on. - * + * * This is the port where all clients will contact this server/site. */ private static int port; /** * The host/IP for a client or server to contact. - * + * * Both client and server who are connecting a server use this host as the host to contact. */ private String remoteHostName = "127.0.0.1"; /** * The port for a client or server to login to/contact. - * + * * Both client and server who are connecting a server use this port as the portt to contact. */ private int remoteHostPort; @@ -249,7 +249,7 @@ public class InternalController implements IInternalController, ITwoToOne, IBtoA private String loginClassName = LOGIN_UI_GUI_CLASSNAME; private String startupDialogClassName = STARTUP_DIALOG_GUI_CLASS; - + private String countdownClassName = COUNTDOWN_UI_CLASSNAME; private IStartupContestDialog startDialog; @@ -261,7 +261,7 @@ public class InternalController implements IInternalController, ITwoToOne, IBtoA /** * Flag indicating whether Roman Numeral shutdown is done. - * + * * If set to false, then will trigger/send the event */ private boolean clientAutoShutdown = true; @@ -276,20 +276,20 @@ public class InternalController implements IInternalController, ITwoToOne, IBtoA private ILogWindow logWindow = null; private RunSubmitterInterfaceManager runSubmitterInterfaceManager = new RunSubmitterInterfaceManager(); - + /** * The object used to control contest auto-starting. */ private AutoStarter autoStarter; - + private AutoStopContestClockThread autoStopContestClockThread = null; - + /** * Packets which should be sent to multiple instances of logins (for example, a "RUN_SUBMSSION_CONFIRMATION" * packet should be sent to every instance of a given team client login when multiple team logins are allowed). * Listing an element of {@link PacketType.Type} in this EnumSet causes packets of that type to be duplicated * and sent to every login instance for the destination client specified in the packet. - * + * * @author John Clevenger, PC2 Development Team (pc2@ecs.csus.edu) * */ @@ -297,8 +297,8 @@ public class InternalController implements IInternalController, ITwoToOne, IBtoA PacketType.Type.RUN_SUBMISSION_CONFIRM, PacketType.Type.RUN_JUDGEMENT, // PacketType.Type.RUN_JUDGEMENT_UPDATE, //this seems to only be sent to judges/admins/boards to update run status, but not teams - PacketType.Type.CLARIFICATION_SUBMISSION_CONFIRM, - PacketType.Type.CLARIFICATION_UPDATE, + PacketType.Type.CLARIFICATION_SUBMISSION_CONFIRM, + PacketType.Type.CLARIFICATION_UPDATE, PacketType.Type.CLARIFICATION_ANSWER, PacketType.Type.CLARIFICATION_ANSWER_UPDATE ); @@ -308,6 +308,7 @@ public InternalController(IInternalContest contest) { setContest(contest); } + @Override public void sendToLocalServer(Packet packet) { if (isThisServer(packet.getSourceId()) && isServer()) { @@ -357,10 +358,11 @@ private void sendToClient(ConnectionHandlerID connectionHandlerID, Packet packet } /** - * + * * @param inSiteNumber site number for client to send to * @param packet info to send */ + @Override public void sendToRemoteServer(int inSiteNumber, Packet packet) { if (packet.getSourceId() != null && (packet.getOriginalSourceId().getSiteNumber() == inSiteNumber)) { @@ -369,9 +371,9 @@ public void sendToRemoteServer(int inSiteNumber, Packet packet) { } Site remoteSite = lookupRemoteServer(inSiteNumber); int siteNumber = remoteSite.getSiteNumber(); - + ClientId clientId = new ClientId(siteNumber, Type.SERVER, 0); - + ConnectionHandlerID connectionHandlerID = getConnectionHandleID(clientId); info("sendToRemoteServer " + clientId + " at "+siteNumber+" " + packet + " " + connectionHandlerID); @@ -397,7 +399,7 @@ public void sendToRemoteServer(int inSiteNumber, Packet packet) { } protected ConnectionHandlerID getConnectionHandleID(ClientId clientId) { - + ClientId[] ids = contest.getLocalLoggedInClients(clientId.getClientType()); for (ClientId id : ids) { if (id.equals(clientId)){ @@ -407,12 +409,13 @@ protected ConnectionHandlerID getConnectionHandleID(ClientId clientId) { return null; } + @Override public void sendToClient(Packet packet) { info("sendToClient b4 to " + packet.getDestinationId() + " " + packet); ClientId toClientId = packet.getDestinationId(); - //this method should never be called with a packet containing a destination ClientId containing a null ConnectionHandlerID; + //this method should never be called with a packet containing a destination ClientId containing a null ConnectionHandlerID; // however, the addition of "multiple login" support may have left some place where this is inadvertently true. //The following is an effort to catch/identify such situations. if (toClientId.getConnectionHandlerID()==null) { @@ -423,14 +426,14 @@ public void sendToClient(Packet packet) { if (isThisSite(toClientId.getSiteNumber())) { if (contest.isLocalLoggedIn(toClientId)) { - + ConnectionHandlerID connectionHandlerID = toClientId.getConnectionHandlerID(); info("sendToClient " + packet.getDestinationId() + " @ " + connectionHandlerID); sendToClient(connectionHandlerID, packet); - + //certain packets should also be sent to any other logged in clients (e.g. logins for the same team) sendToDuplicateClients(packet); - + } else { try { packetArchiver.writeNextPacket(packet); @@ -461,37 +464,37 @@ public void sendToClient(Packet packet) { info("sendToClient af to " + packet.getDestinationId() + " " + packet); } - + /** * Examines the specified packet to determine if there duplicate client logins to which the packet should be sent * (for example, in the case where multiple team clients for the same team have been allowed to login); sends the * specified packet to any duplicate login clients. - * + * * @param packet the packet to be sent to any duplicate login clients. */ private void sendToDuplicateClients(Packet packet) { //determine what client the packet was initially sent to (it is assumed that the caller took care of the initial send) ClientId toClientId = packet.getDestinationId(); - + //see if the packet is one of the types that should be duplicated (sent to all logins for the client) if (DuplicatePacketTypes.contains(packet.getType()) ) { - + //see if the destination for the packet is a team if (toClientId.getClientType()==ClientType.Type.TEAM) { - + //get a list of all logged-in team clients ClientId[] clientList = contest.getAllLoggedInClients(ClientType.Type.TEAM); - + //check every logged-in team client for (ClientId clientId : clientList) { - + //see if the current logged-in team client is the same team as the original intended destination if (clientId.equals(toClientId)) { - + //same team; make sure it's a DIFFERENT connection (we assume the caller already sent the packet to the original dest) if (!clientId.getConnectionHandlerID().equals(toClientId.getConnectionHandlerID())) { - + //it's a different connection for the same team; send a duplicate of the packet ConnectionHandlerID connectionHandlerID = clientId.getConnectionHandlerID(); info("sendToClient " + packet.getDestinationId() + " @ " + connectionHandlerID); @@ -527,15 +530,26 @@ protected void sendToServersAndAdmins(Packet packet) { /** * {@inheritDoc} - * Calling this method is equivalent to calling + * Calling this method is equivalent to calling * {@link #submitJudgeRun(Problem, Language, String, SerializedFile[], long, long)} * with zeroes as the last two parameters. */ - @Override + @Override public void submitJudgeRun(Problem problem, Language language, String filename, SerializedFile[] otherFiles) throws Exception { submitJudgeRun(problem, language, filename, otherFiles, 0, 0); } - + + /** + * {@inheritDoc} + * Calling this method is equivalent to calling + * {@link #submitJudgeRun(Problem, Language, String, SerializedFile[], long, long, boolean)} + * with zeroes as the last two parameters. + */ + @Override + public void submitJudgeRun(Problem problem, Language language, String filename, SerializedFile[] otherFiles, boolean overrideStopOnFailure) throws Exception { + submitJudgeRun(problem, language, filename, otherFiles, 0, 0, overrideStopOnFailure); + } + /** * {@inheritDoc} * Calling this method is equivalent to calling @@ -543,29 +557,58 @@ public void submitJudgeRun(Problem problem, Language language, String filename, * with a {@link SerializedFile} containing the main source code file contents. */ @Override - public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, + public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, + long overrideSubmissionTimeMS, long overrideRunId, boolean overrideStopOnFailure) throws Exception { + + SerializedFile serializedMainFile = new SerializedFile(mainFileName); + + submitJudgeRun(problem, language, serializedMainFile, otherFiles, overrideSubmissionTimeMS, overrideRunId, overrideStopOnFailure); + } + + /** + * {@inheritDoc} + * Calling this method is equivalent to calling + * {@link #submitJudgeRun(Problem, Language, SerializedFile, SerializedFile[], long, long, false)} + * with a {@link SerializedFile} containing the main source code file contents. + */ + @Override + public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, long overrideSubmissionTimeMS, long overrideRunId) throws Exception { SerializedFile serializedMainFile = new SerializedFile(mainFileName); - submitJudgeRun(problem, language, serializedMainFile, otherFiles, overrideSubmissionTimeMS, overrideRunId); + submitJudgeRun(problem, language, serializedMainFile, otherFiles, overrideSubmissionTimeMS, overrideRunId, false); } /** * {@inheritDoc} + * Calling this method is equivalent to calling + * {@link #submitJudgeRun(Problem, Language, SerializedFile, SerializedFile[], long, long, false)} + * with a {@link SerializedFile} containing the main source code file contents. */ @Override - public void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, + public void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, long overrideSubmissionTimeMS, long overrideRunId) throws Exception { + submitJudgeRun(problem, language, mainFile, otherFiles, overrideSubmissionTimeMS, overrideRunId, false); + } + + /** + * {@inheritDoc} + */ + @Override + public void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, + long overrideSubmissionTimeMS, long overrideRunId, boolean overrideStopOnFailure) throws Exception { + ClientId serverClientId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); Run run = new Run(contest.getClientId(), language, problem); RunFiles runFiles = new RunFiles(run, mainFile, otherFiles); - Packet packet = PacketFactory.createSubmittedRun(contest.getClientId(), serverClientId, run, runFiles, overrideSubmissionTimeMS, overrideRunId); + Packet packet = PacketFactory.createSubmittedRun(contest.getClientId(), serverClientId, run, runFiles, overrideSubmissionTimeMS, overrideRunId, overrideStopOnFailure); sendToLocalServer(packet); } + @Override public void requestChangePassword(String oldPassword, String newPassword) { ClientId serverClientId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); @@ -576,7 +619,7 @@ public void requestChangePassword(String oldPassword, String newPassword) { /** * Return int for input string - * + * * @param s * @return zero if error, otherwise returns value. */ @@ -590,7 +633,7 @@ private static int getIntegerValue(String s) { /** * returns true if .ini file exists and key is present in file. - * + * * @see IniFile#getValue(String) * @param key * @return true if key found and ini file exists. @@ -605,7 +648,7 @@ private static boolean containsINIKey(String key) { /** * Get value from .ini file if it exists. - * + * * @param key * @return */ @@ -713,15 +756,16 @@ protected String stripChar(String s, char ch) { /** * Login to contest server. - * + * * @param id * the login name. * @param password * the password for the id. - * @throws SecurityException + * @throws SecurityException * if a login is attempted when the contest is not running or if the login is from a server * and this is a Client (servers can only log in to other servers) */ + @Override public void login(String id, String password) { if (!isStarted) { @@ -765,13 +809,13 @@ public void login(String id, String password) { } info("Contacted using connection id " + remoteServerConnectionHandlerID); - + boolean loggingInSiteRequestsToBeProxy = getBooleanValue(getINIValue(AppConstants.PROXY_ME_SERVER_KEY), false); - + if (parseArguments.isOptPresent(AppConstants.PROXYME_OPTION_STRING)){ loggingInSiteRequestsToBeProxy = true; } - + sendLoginRequestFromServerToServer(connectionManager, remoteServerConnectionHandlerID, clientId, password, loggingInSiteRequestsToBeProxy, 0); } else { @@ -802,12 +846,12 @@ public void login(String id, String password) { sendLoginRequest(connectionManager, clientId, id, password); } } - + /** * For input string return boolean value. - * + * * Does a trim and case in-sensitive match on the list: yes, no, true, false - * + * * @param string true/false string/value * @param defaultBoolean if matches none in list returns this values */ @@ -833,9 +877,9 @@ public boolean getBooleanValue(String string, boolean defaultBoolean) { /** * This is a very temporary kludge class. - * + * * This is used with clientLogin, as a - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -850,15 +894,17 @@ protected class TemporaryClientUI implements UIPlugin, ILoginListener { private SecurityException securityException = null; /** - * + * */ private static final long serialVersionUID = 8735788359720905862L; + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { contest = inContest; controller = inController; } + @Override public String getPluginTitle() { return "TemporaryClientUI"; } @@ -883,25 +929,30 @@ public void setController(IInternalController controller) { this.controller = controller; } + @Override public void loginAdded(LoginEvent event) { // no action } + @Override public void loginRemoved(LoginEvent event) { // no action } + @Override public void loginDenied(LoginEvent event) { securityException = new SecurityException("Login denied " + event.getMessage()); } + @Override public void loginRefreshAll(LoginEvent event) { // no action } } + @Override public IInternalContest clientLogin(IInternalContest internalContest, String loginName, String password) throws Exception { if (!isStarted) { @@ -964,12 +1015,14 @@ public IInternalContest clientLogin(IInternalContest internalContest, String log } } + @Override public void initializeStorage(IStorage storage) { contest.setStorage(storage); packetArchiver = new PacketArchiver(storage, getBaseProfileDirectoryName("packets")); } + @Override public void initializeServer(IInternalContest inContest) throws IOException, ClassNotFoundException, FileSecurityException { if (inContest.getSites().length == 0) { @@ -1038,7 +1091,7 @@ public void initializeServer(IInternalContest inContest) throws IOException, Cla inContest.initializeStartupData(inContest.getSiteNumber()); inContest.initializeSubmissions(inContest.getSiteNumber()); - + if (inContest.getGeneralProblem() == null) { inContest.setGeneralProblem(new Problem("General")); } @@ -1048,24 +1101,24 @@ public void initializeServer(IInternalContest inContest) throws IOException, Cla } info("initialized controller Site " + inContest.getSiteNumber()); - + handleCDPLoad(true); - + if (contest.getJudgements().length == 0) { - // judgements not loaded from yaml, cdp config - load from ./reject.ini + // judgements not loaded from yaml, cdp config - load from ./reject.ini String result = JudgementLoader.loadJudgements(inContest, false, "."); getLog().info(result); } - + if (contest.getJudgements().length == 0) { - // judgements not loaded from cdp, yaml or ./reject.ini + // judgements not loaded from cdp, yaml or ./reject.ini JudgementLoader.loadDefaultJudgements(contest); getLog().info("Loaded default judgements"); } - + dumpAutoStartInformation(contest.getContestInformation()); - + try { manager.mergeProfiles(inContest); } catch (Exception e) { @@ -1106,9 +1159,9 @@ public void initializeServer(IInternalContest inContest) throws IOException, Cla } catch (Exception e) { getLog().log(Log.WARNING, "Exception creating evals.log ", e); } - + updateAutoStartInformation(inContest, this); - + updateAutoStopClockThread(); } @@ -1139,14 +1192,14 @@ private void insureProfileDirectory(Profile profile) { /** * Loads reject.ini file contents into Judgements. - * + * * If finds reject.ini file, reads file. Adds Yes judgement, then prepends "No - " onto each entry from the reject.ini file and returns true. - * + * * Reads judge acronyms if present, if file contains a AC judgement then will use that * text for the Yes judgement. - * + * * Returns false if cannot read reject.ini file or reject.ini file is empty (perhaps only containing comments). - * + * * @return true if loaded, false if could not read file. */ protected boolean loadedJudgementsFromIni(String filename) { @@ -1160,17 +1213,17 @@ protected boolean loadedJudgementsFromIni(String filename) { protected void loadDefaultJudgements() { String[] judgementNames = { // - "Yes", // - "No - Compilation Error", // - "No - Run-time Error", // - "No - Time Limit Exceeded", // - "No - Wrong Answer", // - "No - Excessive Output", // - "No - Output Format Error", // + "Yes", // + "No - Compilation Error", // + "No - Run-time Error", // + "No - Time Limit Exceeded", // + "No - Wrong Answer", // + "No - Excessive Output", // + "No - Output Format Error", // "No - Other - Contact Staff" // }; String [] judgementAcronyms = { - Judgement.ACRONYM_ACCEPTED, // + Judgement.ACRONYM_ACCEPTED, // Judgement.ACRONYM_COMPILATION_ERROR, // Judgement.ACRONYM_RUN_TIME_ERROR, // Judgement.ACRONYM_TIME_LIMIT_EXCEEDED, // @@ -1179,7 +1232,7 @@ protected void loadDefaultJudgements() { Judgement.ACRONYM_OUTPUT_FORMAT_ERROR, // Judgement.ACRONYM_OTHER_CONTACT_STAFF, // }; - + int i = 0; for (String judgementName : judgementNames) { Judgement judgement = new Judgement(judgementName, judgementAcronyms[i]); @@ -1243,11 +1296,11 @@ private Site createFirstSite(int siteNumber, String hostName, int portNumber) { /** * Reads .ini file and sets server and port. - * + * * Sets the server and port for client. - * + * * @param portString - * + * */ private void setClientServerAndPort(String portString) { @@ -1344,9 +1397,9 @@ private void setServerPort(String portString) { /** * Send login request from server to another server. - * + * * Send login request directly to connectionHandlerId. - * + * * @param manager * transmission manager * @param targetConnectionHandlerID @@ -1359,8 +1412,8 @@ private void setServerPort(String portString) { * @param targetSiteNumber the target/destination site number for this login request */ private void sendLoginRequestFromServerToServer(ITransportManager manager, ConnectionHandlerID targetConnectionHandlerID, ClientId clientId, String password, boolean proxyMe, - int targetSiteNumber) { - + int targetSiteNumber) { + try { info("sendLoginRequestFromServerToServer ConId start - sending from " + clientId); ClientId serverClientId = new ClientId(targetSiteNumber, Type.SERVER, 0); @@ -1371,8 +1424,8 @@ private void sendLoginRequestFromServerToServer(ITransportManager manager, Conne } catch (TransportException e) { info("Exception sendLoginRequestFromServerToServer ", e); } - - + + // info("sendLoginRequestFromServerToServer ConId start - sending from " + clientId + " proxyMe = " + proxyMe); // ClientId serverClientId = new ClientId(0, Type.SERVER, 0); // String joeLoginName = password; @@ -1386,7 +1439,7 @@ private void sendLoginRequestFromServerToServer(ITransportManager manager, Conne /** * Send login request to server as a login. - * + * * @param manager * @param clientId * @param password @@ -1401,9 +1454,10 @@ private void sendLoginRequest(ITransportManager manager, ClientId clientId, Stri /** * Server receives Packet from client or server. - * + * * @see edu.csus.ecs.pc2.core.transport.ITwoToOne#receiveObject(java.io.Serializable, edu.csus.ecs.pc2.core.transport.ConnectionHandlerID) */ + @Override public void receiveObject(Serializable object, ConnectionHandlerID connectionHandlerID) { // SOMEDAY SECURITY code check the input connection to insure they are valid connection @@ -1415,7 +1469,7 @@ public void receiveObject(Serializable object, ConnectionHandlerID connectionHan Packet packet = (Packet) object; ClientId clientId = packet.getSourceId(); - + clientId.setConnectionHandlerID(connectionHandlerID); info("receiveObject " + packet); @@ -1439,20 +1493,20 @@ public void receiveObject(Serializable object, ConnectionHandlerID connectionHan } } else if (packet.getType().equals(PacketType.Type.LOGIN_REQUEST)) { - + if (packetHandler.forwardedToProxy((packet))) { - + // Handled proxy packets (send/receive), no additional action necesary in this method. return; } - + String password = PacketFactory.getStringValue(packet, PacketFactory.PASSWORD); boolean requestProxy = false; Boolean requestedProxy = PacketFactory.getBooleanValue(packet, PacketFactory.REQUEST_LOGIN_AS_PROXY); if (requestedProxy != null) { requestProxy = requestedProxy.booleanValue(); } - + try { /** @@ -1462,15 +1516,15 @@ public void receiveObject(Serializable object, ConnectionHandlerID connectionHan packetArchiver.writeNextPacket(packet); if (clientId.getSiteNumber() == ClientId.UNSET) { - + info("LOGIN_REQUEST packet contains clientId object with site number of " + ClientId.UNSET + "; replacing with my site number (" + contest.getSiteNumber() + ")"); - + ConnectionHandlerID connHID = clientId.getConnectionHandlerID(); clientId = new ClientId(contest.getSiteNumber(), clientId.getClientType(), clientId.getClientNumber()); clientId.setConnectionHandlerID(connHID); } attemptToLogin(clientId, password, connectionHandlerID); - + if (requestProxy){ if (isServer(packet.getSourceId())){ /** @@ -1479,7 +1533,7 @@ public void receiveObject(Serializable object, ConnectionHandlerID connectionHan sendSiteUpdateSetProxy (packet.getSourceId(), contest.getSiteNumber()); } } - + sendLoginSuccess(clientId, connectionHandlerID); // Send login notification to users. @@ -1539,16 +1593,16 @@ public void receiveObject(Serializable object, ConnectionHandlerID connectionHan // securityCheck(packet, connectionHandlerID); processPacket(packet, connectionHandlerID); } else { - + if (clientId.getClientType().equals(Type.SERVER)) { // Packet from a server. if (packet.getType() == PacketType.Type.LOGIN_FAILED) { - + handleServerLoginFailure(packet); - + } else if (packet.getType().equals(PacketType.Type.LOGIN_SUCCESS)) { - + /** * The current server has successfully logged into a remote server. */ @@ -1560,14 +1614,14 @@ public void receiveObject(Serializable object, ConnectionHandlerID connectionHan // Add the other (server we logged into) into our local logged in list. loginServer(clientId, connectionHandlerID); - + /** * Since this module is not logged in, this packet should only be a LOGIN_SUCCESS from a server we initially logged into, - * aka the remoteServer. + * aka the remoteServer. */ - + if (connectionHandlerID.equals(remoteServerConnectionHandlerID)){ - + /** * Only accept/change config data on this site if the login success is from the server that this site connected to initially. */ @@ -1575,42 +1629,42 @@ public void receiveObject(Serializable object, ConnectionHandlerID connectionHan // Add data from packet into contest and sync all runs processPacket(packet, connectionHandlerID); - + } else { - + /** * This should happen when a server logs into another server that is not its remoeteServer (initial server logged into). - * + * * This is where the remote site's data is synced on this server. */ - + info("Updating this server's settings from remote server specific settings from " + clientId + " @ " + connectionHandlerID); - + packetHandler.loadSettingsFromRemoteServer(new ContestLoader(), packet, connectionHandlerID); - + info("Sending this server's settings to remote server " + packet.getSourceId() + " @ " + connectionHandlerID); // Send settings packet to the server we logged into sendToClient(packetHandler.createContestSettingsPacket(packet.getSourceId())); - + info("Sending a request for all run files to remote server " + packet.getSourceId() + " @ " + connectionHandlerID); packetHandler.sendRequestForRunfFiles (packet, packet.getSourceId().getSiteNumber()); - + } } else if (contest.isLocalLoggedIn(clientId) && packet.getType().equals(PacketType.Type.LOGIN)) { /** - * A user has logged into another remote server + * A user has logged into another remote server */ // on site 2 login to site 1, site 2 reports LOGIN on login to site1 processPacket(packet, connectionHandlerID); } else if (packet.getType().equals(PacketType.Type.LOGIN) && contest.getSite(contest.getSiteNumber()).hasProxy() && packet.getDestinationId().getSiteNumber() == 0) { /** - * This is probably coming from our proxy + * This is probably coming from our proxy */ processPacket(packet, connectionHandlerID); } else if (contest.isLocalLoggedIn(packet.getDestinationId()) && contest.getSite(contest.getSiteNumber()).getMyProxy() == packet.getDestinationId().getSiteNumber()) { /** - * This is coming from our proxy + * This is coming from our proxy */ processPacket(packet, connectionHandlerID); } else if (contest.isRemoteLoggedIn(packet.getSourceId()) && packet.getSourceId().getClientType() == ClientType.Type.SERVER) { @@ -1648,18 +1702,18 @@ public void receiveObject(Serializable object, ConnectionHandlerID connectionHan /** * Assign proxy, send updated Site to all connected sites. - * + * * @param siteToBeProxied client/site to be proxied * @param proxySiteNumber proxy site for siteToBeProxied */ private void sendSiteUpdateSetProxy(ClientId siteToBeProxied, int proxySiteNumber) { - - // assign proxy + + // assign proxy Site site = contest.getSite(siteToBeProxied.getSiteNumber()); site.setMyProxy(proxySiteNumber); - + updateSite(site); - + } private String[] removeFirstElement(String[] stringArray) { @@ -1670,7 +1724,7 @@ private String[] removeFirstElement(String[] stringArray) { /** * Handle client attempt to auto register. - * + * * @param packet * @param connectionHandlerID */ @@ -1711,6 +1765,7 @@ private void handleAutoRegistration(Packet packet, ConnectionHandlerID connectio } } + @Override public void sendToJudgesAndOthers(Packet packet, boolean sendToServers) { if (isServer()) { @@ -1741,7 +1796,7 @@ private boolean isEnableAutoRegistration() { /** * Creates and saves a ClientSettings. - * + * * @param clientId * @return */ @@ -1769,7 +1824,7 @@ private ClientSettings getClientSettings(ClientId clientId) { /** * This logs server into local logins and out of remote logins, if needed. - * + * * @param clientId * @param connectionHandlerID */ @@ -1828,7 +1883,7 @@ private void securityCheck(Packet packet, ConnectionHandlerID connectionHandlerI /** * Handle incoming invalid login and message. - * + * * @param packet */ private void handleServerLoginFailure(Packet packet) { @@ -1861,7 +1916,7 @@ private void handleServerLoginFailure(Packet packet) { /** * Looks up site number based on password. - * + * * @param password * @return site number or throws SecurityException if nothing matches. */ @@ -1913,7 +1968,7 @@ public static boolean matchOverride(String password) { } return false; } - + /** * Returns true if the password matches the hash for the override password. */ @@ -1955,9 +2010,9 @@ protected boolean validAccountAndMatchOverride(ClientId clientId, String passwor /** * Attempt to login, if login success add to login list. - * + * * If login fails will throw SecurityException. - * + * * @param newClientId the new client attempting to log in. * @param password the new client's password. * @param connectionHandlerID the ConnectionHandlerID associated with the newly connecting client. @@ -1965,13 +2020,13 @@ protected boolean validAccountAndMatchOverride(ClientId clientId, String passwor private void attemptToLogin(ClientId newClientId, String password, ConnectionHandlerID connectionHandlerID) { info("ClientID=" + newClientId); - + if (newClientId.getClientType().equals(Type.SERVER)) { // Server login request int newSiteNumber = getServerSiteNumber(newClientId.getSiteNumber(), password); - - info("Logging in new server; newSiteNumber = " + newSiteNumber); + + info("Logging in new server; newSiteNumber = " + newSiteNumber); info("My site number = " + contest.getSiteNumber()); if (newSiteNumber == contest.getSiteNumber()) { @@ -1992,24 +2047,24 @@ private void attemptToLogin(ClientId newClientId, String password, ConnectionHan } else { // Client login request - + //check if the account and password are valid if (validAccountAndMatchOverride(newClientId, password) || contest.isValidLoginAndPassword(newClientId, password)) { //valid acct and pw; see if the account is already logged in if (contest.isLocalLoggedIn(newClientId)) { - + //already logged in; see if the current login(s) should be forced off if (newClientId.getClientType()!=ClientType.Type.TEAM || (!contest.getContestInformation().isAllowMultipleLoginsPerTeam())) { - + //not a team, or is a team but multiple team logins not allowed; force off all clients matching the new client forceOffAllClientsMatching(newClientId); } } - + contest.addLocalLogin(newClientId, connectionHandlerID); info("LOGIN logged in " + newClientId + " at " + connectionHandlerID); - + } else { //invalid account or bad password info("attemptToLogin FAILED logged on: " + newClientId); @@ -2019,46 +2074,46 @@ private void attemptToLogin(ClientId newClientId, String password, ConnectionHan } } } - + /** - * Searches the list of currently logged-in clients and forces a logoff for any client matching + * Searches the list of currently logged-in clients and forces a logoff for any client matching * the specified ClientId (that is, any client of the same type, number, and site). - * + * * @param clientId the ClientId for which matching client logins are to be forced logged-off. */ private void forceOffAllClientsMatching(ClientId clientId) { // get a list of all logged in clients of the same type as the specified client ClientId[] loggedInClientsOfSameType = contest.getLocalLoggedInClients(clientId.getClientType()); - + //check every logged in client in the list for (ClientId nextLoggedInClientId : loggedInClientsOfSameType) { - + //see if the next logged in client "matches" the specified incoming client. //(note that ClientId.equals() compares type, number, and site (only)) if (nextLoggedInClientId.equals(clientId)) { - + //yes, matches; force the existing client to logoff forceLogoff(nextLoggedInClientId); } } } - + /** * This method forces a logoff of the specified Client. - * + * * @param clientId the client that is to be forced logged off. */ private void forceLogoff(ClientId clientId) { - + // Already logged in, log them off log.info("login - " + clientId + " already logged in at connection " + clientId.getConnectionHandlerID() + "; forcing logoff " ); - + // old code: only removes login from local contest but doesn't notify Admins or Servers // // this updates the model contest-wide // contest.removeLogin(clientId); - + //new code: calls contest.removeLogin(clientId) as above, but also calls forceConnectionDrop() and sends LOGOFF packets to Admins and Servers logoffUser(clientId); @@ -2070,7 +2125,7 @@ private void forceLogoff(ClientId clientId) { log.log(Log.WARNING, "Exception on canceling runs/clars for " + clientId, e); } } - + // old code; not needed now because the call to logoffUser() (above) calls forceConnectionDrop() // //this is what actually causes the connection to be dropped/disconnected // forceConnectionDrop(connHandlerID); @@ -2085,7 +2140,7 @@ private void forceLogoff(ClientId clientId) { /** * Can this user checkout clars and runs. - * + * * @param theClient * @return */ @@ -2095,11 +2150,11 @@ protected boolean canCheckoutRunsAndClars(ClientId theClient) { /** * Process all packets. - * + * * Assumes that the packet is from an authenticated user. - * + * * Process packets when user is logged in. - * + * * @param packet * @param connectionHandlerID */ @@ -2176,7 +2231,7 @@ private void processPacket(Packet packet, ConnectionHandlerID connectionHandlerI /** * Send login failure packet back to non-logged in user, via ConnectionHandlerID. - * + * * @param destinationId * @param connectionHandlerID * @param message @@ -2188,7 +2243,7 @@ private void sendLoginFailure(ClientId destinationId, ConnectionHandlerID connec /** * Send Login Success packet to client. - * + * * @param clientId * @param connectionHandlerID */ @@ -2197,6 +2252,7 @@ private void sendLoginSuccess(ClientId clientId, ConnectionHandlerID connectionH sendToClient(packetHandler.createLoginSuccessPacket(clientId, contest.getContestPassword())); } + @Override public void connectionEstablished(ConnectionHandlerID connectionHandlerID) { info("connectionEstablished: " + connectionHandlerID); contest.connectionEstablished(connectionHandlerID); @@ -2208,9 +2264,10 @@ public void connectionEstablished(ConnectionHandlerID connectionHandlerID) { /** * Connection to client lost. - * + * * @throws IOException */ + @Override public void connectionDropped(ConnectionHandlerID connectionHandlerID) { // getLog().log(Log.INFO, "connection Dropped for " + connectionHandlerID, new Exception("connection Dropped for " + connectionHandlerID)); @@ -2258,7 +2315,7 @@ protected void cancelAllClarsByThisJudge(ClientId judgeId) throws ContestSecurit /** * Cancel all checked out runs and clarifications by this client. - * + * * @param judgeId * @throws ContestSecurityException * @throws FileSecurityException @@ -2296,6 +2353,7 @@ protected void cancelAllRunsByThisJudge(ClientId judgeId) { /** * @throws IllegalArgumentException if the specified ClientId contains a null ConnectionHandlerID. */ + @Override public void logoffUser(ClientId clientId) { if (isServer() && contest.isLocalLoggedIn(clientId)) { @@ -2307,7 +2365,7 @@ public void logoffUser(ClientId clientId) { ConnectionHandlerID connectionHandlerID = clientId.getConnectionHandlerID(); - //this method should never be called with a clientId having a null ConnectionHandlerID; however, the addition of + //this method should never be called with a clientId having a null ConnectionHandlerID; however, the addition of // "multiple login" support may have left some place where this is inadvertently true. //The following is an effort to catch/identify such situations. if (connectionHandlerID==null) { @@ -2316,7 +2374,7 @@ public void logoffUser(ClientId clientId) { logException("InternalController.logoffUser() called null ConnectionHandlerID in ClientId " + clientId, e); throw e; } - + contest.removeLogin(clientId); @@ -2335,6 +2393,7 @@ public void logoffUser(ClientId clientId) { } + @Override public void connectionError(Serializable object, ConnectionHandlerID connectionHandlerID, String causeDescription) { // SOMEDAY code create a packet and send it to servers and admins @@ -2346,9 +2405,10 @@ public void connectionError(Serializable object, ConnectionHandlerID connectionH /** * Client receive object. - * + * * @see edu.csus.ecs.pc2.core.transport.IBtoA#receiveObject(java.io.Serializable) */ + @Override public void receiveObject(Serializable object) { info(" receiveObject(S) start got " + object); @@ -2363,14 +2423,14 @@ public void receiveObject(Serializable object) { // SOMEDAY code put the server's connection handler id as 4th parameter packetHandler.handlePacket(packet, null); - + } else { info("receiveObject(S) Unsupported class received: " + object.getClass().getName()); } } catch (Exception e) { - + error(e.toString(),e); - + if (loginUI != null) { loginUI.regularCursor(); } @@ -2381,6 +2441,7 @@ public void receiveObject(Serializable object) { /** * This client lost connection. */ + @Override public void connectionDropped() { // Connection dropped, countdown and halt client @@ -2401,19 +2462,19 @@ public void connectionDropped() { contest.connectionDropped(null); } } - + public void setCountdownClassName(String countdownClassName) { this.countdownClassName = countdownClassName; } - + public String getCountdownClassName() { return countdownClassName; } - + public void setLoginClassName(String loginClassName) { this.loginClassName = loginClassName; } - + public String getLoginClassName() { return loginClassName; } @@ -2424,7 +2485,7 @@ public void setStartupDialogClassName(String startupDialogClassName) { public String getStartupDialogClassName() { return startupDialogClassName; - } + } private void shutdown() { @@ -2442,7 +2503,7 @@ private void shutdown() { */ countDownMessage = new TextCountDownMessage(); } - + if (contest.getClientId() != null) { info("connectionDropped: shutting down " + contest.getClientId()); countDownMessage.setTitle("Shutting down PC^2 " + contest.getClientId().getClientType() + " " + contest.getTitle()); @@ -2452,13 +2513,13 @@ private void shutdown() { } countDownMessage.setExitOnClose(true); countDownMessage.start("Shutting down PC^2 in ", 10); - + /** - * This is needed to allow the countdown timer threads to - * actually run, otherwise the JVM ends before the timer starts. + * This is needed to allow the countdown timer threads to + * actually run, otherwise the JVM ends before the timer starts. */ sleep (12); - + } /** @@ -2466,22 +2527,22 @@ private void shutdown() { * @param secs */ private void sleep(int secs) { - + try { Thread.sleep(secs * 1000); } catch (InterruptedException e) { e.printStackTrace(); } - + } - + public void info(String message) { if (! isUsingGUI()){ System.out.println(message); } log.log(Log.INFO, message); } - + public void error(String message, Exception ex){ if (! isUsingGUI()){ System.err.println(message+" exception "+ex.getMessage()); @@ -2490,7 +2551,7 @@ public void error(String message, Exception ex){ } public void info(String message, Exception exception) { - + if (! isUsingGUI()){ System.out.println(message); } @@ -2501,10 +2562,12 @@ public void info(String message, Exception exception) { log.log(Log.INFO, message, exception); } + @Override public void setSiteNumber(int number) { contest.setSiteNumber(number); } + @Override public void setContestTime(ContestTime contestTime) { if (contest.getContestTime() != null) { contest.updateContestTime(contestTime); @@ -2513,6 +2576,7 @@ public void setContestTime(ContestTime contestTime) { } } + @Override public void sendToServers(Packet packet) { // do remotes first, for proxies this mean they are shutdown before the server @@ -2550,25 +2614,25 @@ public void sendToServers(Packet packet) { /** * Send packet to all clients logged into this site that are a particular clienttype. - * + * * @param packet * @param type client type */ private void sendPacketToClients(Packet packet, ClientType.Type type) { ClientId[] clientIds = contest.getLocalLoggedInClients(type); - + List badClients = new ArrayList(); - + for (ClientId clientId : clientIds) { - + if (isThisSite(clientId.getSiteNumber())) { - + ConnectionHandlerID connectionHandlerID = null; try { connectionHandlerID = clientId.getConnectionHandlerID(); - - //there should never be localLoggedInClients with null ConnectionHandlerIDs; however, the addition of + + //there should never be localLoggedInClients with null ConnectionHandlerIDs; however, the addition of // "multiple login" support may have left some place where this is inadvertently true. //The following is an effort to catch/identify such situations. if (connectionHandlerID==null) { @@ -2576,13 +2640,13 @@ private void sendPacketToClients(Packet packet, ClientType.Type type) { badClients.add(clientId); } else { //good clientId; send the packet to the client - sendToClient(connectionHandlerID, packet); + sendToClient(connectionHandlerID, packet); } } catch (Exception e) { getLog().log(Level.WARNING, "Exception attempting to send packet to client " + clientId + "at connectionHandlerId " + connectionHandlerID + ": " + packet + ": "+ e.getMessage(), e); } - + //check if we got any bad clientIds if (badClients.size()>0) { //yes, build a comma-separated list of bad clientIds @@ -2599,7 +2663,7 @@ private void sendPacketToClients(Packet packet, ClientType.Type type) { e.printStackTrace(); throw e; } - + } } } @@ -2610,29 +2674,34 @@ private boolean isThisSite(int siteNumber) { /** * Send to judges and spectators clients. - * + * */ + @Override public void sendToJudges(Packet packet) { sendPacketToClients(packet, ClientType.Type.JUDGE); sendPacketToClients(packet, ClientType.Type.SPECTATOR); } + @Override public void sendToSpectators(Packet packet) { sendPacketToClients(packet, ClientType.Type.SPECTATOR); } + @Override public void sendToAdministrators(Packet packet) { sendPacketToClients(packet, ClientType.Type.ADMINISTRATOR); } + @Override public void sendToScoreboards(Packet packet) { sendPacketToClients(packet, ClientType.Type.SCOREBOARD); } - + public void sendToFeeders(Packet packet) { sendPacketToClients(packet, ClientType.Type.FEEDER); } + @Override public void sendToTeams(Packet packet) { Properties properties = (Properties) packet.getContent(); @@ -2681,10 +2750,11 @@ private int getPortForSite(int inSiteNumber) { /** * Client has successfully logged in, show them UI. - * + * * @param clientId * new client id */ + @Override public void startMainUI(ClientId clientId) { try { @@ -2739,7 +2809,7 @@ public void startMainUI(ClientId clientId) { info("Loaded UI class " + uiClassName); } } - + uiPlugin.setContestAndController(contest, this); if (loginUI != null) { @@ -2770,95 +2840,95 @@ public void startMainUI(ClientId clientId) { * @param allowedToLoadConfig if true will overwrite config with CDP, else does nothing. */ protected void handleCDPLoad(boolean allowedToLoadConfig) { - + /** * Name of CDP dir or file to be loaded. */ String entryLocation = contest.getCommandLineOptionValue(AppConstants.LOAD_OPTION_STRING); - + if (contest.isCommandLineOptionPresent(AppConstants.LOAD_OPTION_STRING)){ - + if (allowedToLoadConfig){ - + IContestLoader loader = new ContestSnakeYAMLLoader(); info("Loading CDP from " + entryLocation); - + try { - + // Load Configuration from CDP - + loader.initializeContest(contest, new File(entryLocation)); - + info("Loaded CDP/config values from " + entryLocation); - + File cdpConfigDir = loader.findCDPConfigDirectory(new File(entryLocation)); - + if (contest.getJudgements().length == 0) { // judgements not loaded from yaml nor config/reject.ini // load from "." JudgementLoader.loadJudgements(contest, false, "."); } - - - + + + if (saveCofigurationToDisk) { // save newly merged profiles contest.storeConfiguration(getLog()); } - + // Load Submissions from CDP - + String submissionsDir = entryLocation + File.separator + IContestLoader.SUBMISSIONS_DIRNAME; - - String eventFeedDirectory = entryLocation + File.separator + IContestLoader.EVENT_FEED_DIRNAME; + + String eventFeedDirectory = entryLocation + File.separator + IContestLoader.EVENT_FEED_DIRNAME; String eventFeedfilename = eventFeedDirectory + File.separator + IContestLoader.EVENT_FEED_XML_FILENAME; - - + + if (new File(submissionsDir).isDirectory()){ - + if (new File(eventFeedfilename).isFile()){ - + info("Loading event feed runs... "); info("Event feed file is "+eventFeedfilename); - + LoadRuns loadRuns = new LoadRuns(); - + List eventFeedRuns = loadRuns.loadRunsFromEventFeed(eventFeedfilename); - + info("Found "+eventFeedRuns.size()+" runs."); - + info("Validating event feed runs"); - + loadRuns.validateEventFeedRuns(contest, eventFeedRuns); - + info("Validated "+eventFeedRuns.size()+" event feed runs."); - + info("Loading "+eventFeedRuns.size()+" event feed runs."); - + boolean addRuneJudgementsFromEventFeed = contest.isCommandLineOptionPresent(AppConstants.LOAD_EF_JUDGEMENTS_OPTION_STRING); - + loadRuns.updateContestFromEFRuns(contest, eventFeedRuns, cdpConfigDir.getParent(), addRuneJudgementsFromEventFeed); - + info("Loaded "+eventFeedRuns.size()+" event feed runs."); if (addRuneJudgementsFromEventFeed){ info("Loaded judgments for "+eventFeedRuns.size()+" event feed runs."); } else { info("No judgements were added for "+eventFeedRuns.size()+" event feed runs, all are 'new' runs."); } - + eventFeedRuns = null; loadRuns = null; - + } else { info("No submissions loaded, no event feed at "+eventFeedfilename); } - + } else { info("No submissions loaded, no submissions at "+submissionsDir); } - - - + + + } catch (Exception e) { String szMsg = e.getMessage(); if(szMsg == null) { @@ -2869,17 +2939,17 @@ protected void handleCDPLoad(boolean allowedToLoadConfig) { loader = null; } } else { - + info("** Not loading (" + AppConstants.LOAD_OPTION_STRING + ") CDP from " + entryLocation+" contest config ALREADY exists. **"); - System.err.println ("Warning: contest configuration already exists; ignoring " + AppConstants.LOAD_OPTION_STRING + " option"); - + System.err.println ("Warning: contest configuration already exists; ignoring " + AppConstants.LOAD_OPTION_STRING + " option"); + } } } /** * Dump Auto Start information to log. - * + * * @param contestInformation */ private void dumpAutoStartInformation(ContestInformation contestInformation) { @@ -2898,19 +2968,20 @@ private void dumpAutoStartInformation(ContestInformation contestInformation) { /** * Start the UI. */ + @Override public void start(String[] stringArray) { /** * Saved exception. - * + * * If TransportException thrown before UI has been created, save the exception and present it on the UI later. */ TransportException savedTransportException = null; - String[] requireArgumentArgs = { // - AppConstants.LOGIN_OPTION_STRING, + String[] requireArgumentArgs = { // + AppConstants.LOGIN_OPTION_STRING, AppConstants.PASSWORD_OPTION_STRING, - AppConstants.MAIN_UI_OPTION_STRING, + AppConstants.MAIN_UI_OPTION_STRING, AppConstants.REMOTE_SERVER_OPTION_STRING, // AppConstants.PORT_OPTION_STRING, // AppConstants.LOAD_OPTION_STRING, // @@ -2919,7 +2990,7 @@ public void start(String[] stringArray) { AppConstants.CONTEST_PASSWORD_OPTION_STRING, // AppConstants.CONTEST_ID_OPTION_STRING, AppConstants.FILE_OPTION_STRING }; - + /** * This will not catch unknown options, that is done below after logging is set up * We scan the arguments twice. First time to allow logging to be set up @@ -2931,24 +3002,24 @@ public void start(String[] stringArray) { // mostly catches "IllegalArgumentException" and informs the user fatalError(e.getMessage(), e); } - + /** * usingGUI should be set early no matter what, since we do not want to pop-up * GUI message boxes on errors, if NO_GUI_OPTION_STRING was specified. * Validation of NO_GUI_OPTION_STRING is handled in handleCommandLineOptions(). * That is, currently, NO_GUI_OPTION_STRING is only allowed for AJ and server. - * We don't care about that at this point in time though. + * We don't care about that at this point in time though. */ if (parseArguments.isOptPresent(AppConstants.NO_GUI_OPTION_STRING)) { usingGUI = false; } - + if (parseArguments.isOptPresent(AppConstants.DEBUG_OPTION_STRING)){ parseArguments.dumpArgs(System.out); } - + contest.setCommandLineArguments(parseArguments); - + if (parseArguments.isOptPresent(AppConstants.SERVER_OPTION_STRING)) { if (!isContactingRemoteServer()) { theProfile = getCurrentProfile(); @@ -2961,7 +3032,7 @@ public void start(String[] stringArray) { } else { startLog(null, "pc2.startup."+System.currentTimeMillis(), null, null); } - + // make sure supplied arguments are recognized (allowed) try { // this is the second scan of the argument list where accepted options are checked. @@ -2986,7 +3057,7 @@ public void start(String[] stringArray) { /** * if (args DOES NOT contains login/pwd) { String s; if (args contains LoginUI ) { s = args login UI } else { s = pc2 LoginFrame } UIPlugin l = classloader (s); l.setModelAndListener (contest, * this); } else { this.login (login,password) - * + * */ log.info("Starting ConnectionManager..."); @@ -3022,7 +3093,7 @@ public void start(String[] stringArray) { fatalError("Cannot start PC^2, " + iniName + " cannot be read (" + exception.getMessage() + ")", exception); } //the following line was deleted when InternalController was updated per b_1564_integrate_API_work -// IniFile.setHashtable(ini.getHashmap()); +// IniFile.setHashtable(ini.getHashmap()); } contest.setSiteNumber(0); @@ -3061,7 +3132,7 @@ public void start(String[] stringArray) { try { setServerPort(parseArguments.getOptValue(AppConstants.PORT_OPTION_STRING)); } catch (NumberFormatException numException) { - savedTransportException = new TransportException("Unable to parse value after " + + savedTransportException = new TransportException("Unable to parse value after " + AppConstants.PORT_OPTION_STRING + "'" + parseArguments.getOptValue(AppConstants.PORT_OPTION_STRING) + "'"); log.log(Log.WARNING, "Exception parsing " + AppConstants.PORT_OPTION_STRING, numException); } @@ -3176,7 +3247,7 @@ private UIPlugin loadUIClass(String className) { /** * Get current profile, create one if needed. - * + * * @return */ private Profile getCurrentProfile() { @@ -3218,7 +3289,7 @@ private void handleCommandLineOptions() { "[" + AppConstants.FIRST_SERVER_OPTION_STRING + "] " + // "[" + AppConstants.LOGIN_OPTION_STRING + " ] " + // "[" + AppConstants.PASSWORD_OPTION_STRING + " ]", // - " " + + " " + "[" + AppConstants.LOAD_OPTION_STRING + "

| ] " + // "[" + AppConstants.SKIP_INI_OPTION_STRING + "] " + // "[" + AppConstants.INI_FILENAME_OPTION_STRING + " INIfilename] " + // @@ -3230,11 +3301,11 @@ private void handleCommandLineOptions() { " " + "[" + AppConstants.NOLOGGING_OPTION_STRING + "] " + // "[" + AppConstants.NO_GUI_OPTION_STRING + "] " + // - "[" + AppConstants.NOSTANDINGS_OPTION_STRING + "] " + - "[" + AppConstants.NO_CONNECTIONS_PANE_OPTION_STRING + "] " + + "[" + AppConstants.NOSTANDINGS_OPTION_STRING + "] " + + "[" + AppConstants.NO_CONNECTIONS_PANE_OPTION_STRING + "] " + "[" + AppConstants.NO_LOGINS_PANE_OPTION_STRING + "]", // "", // - "See https://github.com/pc2ccs/pc2v9/wiki/Command-Line-Arguments for more information", // + "See https://github.com/pc2ccs/pc2v9/wiki/Command-Line-Arguments for more information", // }; for (String string : usage) { System.out.println(string); @@ -3259,7 +3330,7 @@ private void handleCommandLineOptions() { fatalError("Unable to read file " + propertiesFileName, e); } } - + if (parseArguments.isOptPresent(AppConstants.NOSTANDINGS_OPTION_STRING)) { Utilities.setShowStandingsPanes(false); } @@ -3297,7 +3368,7 @@ private void handleCommandLineOptions() { } else if (isScoreboard(client)){ overRideUIName = "edu.csus.ecs.pc2.ui.board.ScoreboardModule"; } else if (isEventFeeder(client)) { - overRideUIName = "edu.csus.ecs.pc2.services.eventFeed.EventFeederModule"; + overRideUIName = "edu.csus.ecs.pc2.services.eventFeed.EventFeederModule"; } else { fatalError(AppConstants.NO_GUI_OPTION_STRING + " can only be used with a judge, server, scoreboard or event feed login, login '" + loginName + "' is not one of those."); } @@ -3359,18 +3430,18 @@ private boolean isEventFeeder(ClientId clientId) { /** * Print debug message to stdout. - * + * * @param string */ protected void printDebug(String message) { System.out.println("debug: "+message); - + } private boolean isJudge(ClientId clientId) { return clientId.getClientType().equals(ClientType.Type.JUDGE); } - + private boolean isScoreboard(ClientId clientId) { return clientId.getClientType().equals(ClientType.Type.SCOREBOARD); } @@ -3382,9 +3453,9 @@ private ClientId getServerClientId() { /** * Return working directory. - * + * * File.separator has already been appended as needed. - * + * * @param dirname * @return profile directory if server, else return ""; */ @@ -3399,9 +3470,9 @@ private String getBaseProfileDirectoryName(String dirname) { /** * Start new Log for client/server. - * + * * This new a new Log(logFileName), sets up the StaticLog, and prints basic info to the log. If loginName is not null a Login: line is printed. - * + * * @param directoryName * if null will use the profile/* directory. * @param logFileName @@ -3435,12 +3506,14 @@ private void startLog(String directoryName, String logFileName, String loginName } } + @Override public void checkOutRun(Run run, boolean readOnly, boolean computerJudge) { ClientId clientId = contest.getClientId(); Packet packet = PacketFactory.createRunRequest(clientId, getServerClientId(), run, clientId, readOnly, computerJudge); sendToLocalServer(packet); } + @Override public void checkOutRejudgeRun(Run run) { ClientId clientId = contest.getClientId(); Packet packet = PacketFactory.createRunRejudgeRequest(clientId, getServerClientId(), run, clientId); @@ -3450,6 +3523,7 @@ public void checkOutRejudgeRun(Run run) { /** * Send run judgement to server. */ + @Override public void submitRunJudgement(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles) { ClientId clientId = contest.getClientId(); Packet packet = PacketFactory.createRunJudgement(clientId, getServerClientId(), run, judgementRecord, runResultFiles); @@ -3459,6 +3533,7 @@ public void submitRunJudgement(Run run, JudgementRecord judgementRecord, RunResu /** * Send cancel run to server. */ + @Override public void cancelRun(Run run) { ClientId clientId = contest.getClientId(); Packet packet = PacketFactory.createUnCheckoutRun(clientId, getServerClientId(), run, clientId); @@ -3467,11 +3542,12 @@ public void cancelRun(Run run) { /** * Add a new site into contest, send update to other servers. - * + * * @throws FileSecurityException * @throws ClassNotFoundException * @throws IOException */ + @Override public void addNewSite(Site site) { if (isServer()) { contest.addSite(site); @@ -3511,6 +3587,7 @@ public void modifySite(Site site) { sendToServers(packet); } + @Override public void sendServerLoginRequest(int inSiteNumber) throws Exception { if (isServer()) { @@ -3525,7 +3602,7 @@ public void sendServerLoginRequest(int inSiteNumber) throws Exception { } Site remoteSite = lookupRemoteServer(inSiteNumber); - + Site localSite = contest.getSite(contest.getSiteNumber()); String localPassword = localSite.getPassword(); @@ -3554,7 +3631,7 @@ public void sendServerLoginRequest(int inSiteNumber) throws Exception { info("Contacted Site " + inSiteNumber + " (via " + remoteSite.getSiteNumber() + ") using connection id " + connectionHandlerID); sendLoginRequestFromServerToServer(connectionManager, connectionHandlerID, getServerClientId(), localPassword, false, inSiteNumber); - + } else if (contest.isAllowed(Permission.Type.ALLOWED_TO_RECONNECT_SERVER)) { // Send the reconnection request to our server @@ -3571,9 +3648,9 @@ public void sendServerLoginRequest(int inSiteNumber) throws Exception { /** * Determine which site to send this packet to. - * + * * This handles packets sent via proxy. - * + * * @param inSiteNumber destination site * @return site to send packet to. */ @@ -3584,8 +3661,8 @@ protected Site lookupRemoteServer(int inSiteNumber) { */ Site site = contest.getSite(inSiteNumber); Site mySite = contest.getSite(contest.getSiteNumber()); - - // if the destination has a proxy + + // if the destination has a proxy if (site.hasProxy()) { /** @@ -3599,14 +3676,14 @@ protected Site lookupRemoteServer(int inSiteNumber) { } else if (mySite.hasProxy()) { site = contest.getSite(mySite.getMyProxy()); }// else no proxys, send to site - + // System.out.println("debug lookupRemoteServer for site "+inSiteNumber+" use site "+site); return site; } /** * Contacting remote server (joining contest). - * + * * @return true if joining contest, false if first server */ public boolean isContactingRemoteServer() { @@ -3619,9 +3696,9 @@ public void setContactingRemoteServer(boolean contactingRemoteServer) { /** * Will main UI be invoked/displayed ? - * + * * This includes Login UI as well as Main UI. - * + * * @return true - shows main UI, false - does not show main UI. */ public boolean isUsingMainUI() { @@ -3640,6 +3717,7 @@ public void setUiPlugin(UIPlugin uiPlugin) { this.uiPlugin = uiPlugin; } + @Override public void updateSite(Site site) { if (isServer()) { @@ -3670,7 +3748,7 @@ public void updateSite(Site site) { /** * Returns true if this client is a server. - * + * * @return true if logged in client is a server. */ private boolean isServer() { @@ -3681,21 +3759,25 @@ private boolean isServer(ClientId clientId) { return clientId.getClientType().equals(ClientType.Type.SERVER); } + @Override public final Log getLog() { return log; } + @Override public void generateNewAccounts(String clientTypeName, int siteNumber, int count, int startNumber, boolean active) { ClientType.Type type = ClientType.Type.valueOf(clientTypeName); Packet packet = PacketFactory.createGenerateAccounts(contest.getClientId(), getServerClientId(), siteNumber, type, count, startNumber, active); sendToLocalServer(packet); } + @Override public void generateNewAccounts(String clientTypeName, int count, int startNumber, boolean active) { generateNewAccounts(clientTypeName, contest.getSiteNumber(), count, startNumber, active); } + @Override public void submitClarification(Problem problem, String question) { ClientId serverClientId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); @@ -3706,27 +3788,31 @@ public void submitClarification(Problem problem, String question) { sendToLocalServer(packet); } + @Override public void checkOutClarification(Clarification clarification, boolean readOnly) { ClientId serverClientId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); Packet packet = PacketFactory.createClarificationRequest(contest.getClientId(), serverClientId, clarification.getElementId(), contest.getClientId()); sendToLocalServer(packet); } + @Override public void cancelClarification(Clarification clarification) { ClientId serverClientId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); Packet packet = PacketFactory.createUnCheckoutClarification(contest.getClientId(), serverClientId, clarification); sendToLocalServer(packet); } + @Override public void submitClarificationAnswer(Clarification clarification) { ClientId serverClientId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); Packet packet = PacketFactory.createAnsweredClarification(contest.getClientId(), serverClientId, clarification, clarification.getAnswer()); sendToLocalServer(packet); } + @Override public void forceConnectionDrop(ConnectionHandlerID connectionHandlerID) { - //this method should never be called with a null ConnectionHandlerID; however, the addition of + //this method should never be called with a null ConnectionHandlerID; however, the addition of // "multiple login" support may have left some place where this is inadvertently true. //The following is an effort to catch/identify such situations. if (connectionHandlerID==null) { @@ -3735,7 +3821,7 @@ public void forceConnectionDrop(ConnectionHandlerID connectionHandlerID) { logException("InternalController.forceConnectionDrop() called with null ConnectionHandlerID", e); throw e; } - + if (isServer()) { if (contest.isConnected(connectionHandlerID)) { @@ -3755,40 +3841,48 @@ public void forceConnectionDrop(ConnectionHandlerID connectionHandlerID) { } } + @Override public void addNewProblem(Problem problem, ProblemDataFiles problemDataFiles) { Packet updateProblemPacket = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), problem, problemDataFiles); sendToLocalServer(updateProblemPacket); } + @Override public void addNewProblem(Problem[] problem, ProblemDataFiles[] problemDataFiles) { Packet updateProblemPacket = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), problem, problemDataFiles); sendToLocalServer(updateProblemPacket); } + @Override public void updateRun(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles) { Packet updateRunPacket = PacketFactory.createRunUpdated(contest.getClientId(), getServerClientId(), run, judgementRecord, runResultFiles, contest.getClientId()); sendToLocalServer(updateRunPacket); } + @Override public void addProblem(Problem problem) { Packet updateProblemPacket = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), problem, null); sendToLocalServer(updateProblemPacket); } + @Override public void updateProblem(Problem problem) { Packet updateProblemPacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), problem, null); sendToLocalServer(updateProblemPacket); } + @Override public void updateProblem(Problem problem, ProblemDataFiles problemDataFiles) { Packet updateProblemPacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), problem, problemDataFiles); sendToLocalServer(updateProblemPacket); } + @Override public ProblemDataFiles getProblemDataFiles(Problem problem) { return contest.getProblemDataFile(problem); } + @Override public void shutdownTransport() { connectionManager.shutdownTransport(); } @@ -3796,6 +3890,7 @@ public void shutdownTransport() { /** * Removes connection from list and sends packet. */ + @Override public void removeConnection(ConnectionHandlerID connectionHandlerID) { contest.connectionDropped(connectionHandlerID); @@ -3809,6 +3904,7 @@ public void removeConnection(ConnectionHandlerID connectionHandlerID) { /** * Removed login from system and sends packet. */ + @Override public void removeLogin(ClientId clientId) { contest.removeLogin(clientId); @@ -3825,66 +3921,79 @@ public void removeLogin(ClientId clientId) { } } + @Override public void startContest(int inSiteNumber) { Packet packet = PacketFactory.createStartContestClock(contest.getClientId(), getServerClientId(), inSiteNumber, contest.getClientId()); sendToLocalServer(packet); } + @Override public void stopContest(int inSiteNumber) { Packet packet = PacketFactory.createStopContestClock(contest.getClientId(), getServerClientId(), inSiteNumber, contest.getClientId()); sendToLocalServer(packet); } + @Override public void startAllContestTimes() { Packet packet = PacketFactory.createStartAllClocks(contest.getClientId(), getServerClientId(), contest.getClientId()); sendToLocalServer(packet); } + @Override public void stopAllContestTimes() { Packet packet = PacketFactory.createStopAllClocks(contest.getClientId(), getServerClientId(), contest.getClientId()); sendToLocalServer(packet); } + @Override public void addNewLanguage(Language language) { Packet addLanguagePacket = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), language); sendToLocalServer(addLanguagePacket); } + @Override public void addNewJudgement(Judgement judgement) { Packet addJudgementPacket = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), judgement); sendToLocalServer(addJudgementPacket); } + @Override public void updateLanguage(Language language) { Packet updateLanguagePacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), language); sendToLocalServer(updateLanguagePacket); } + @Override public void updateJudgement(Judgement judgement) { Packet updatePacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), judgement); sendToLocalServer(updatePacket); } + @Override public void updateAccount(Account account) { Packet updatePacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), account); sendToLocalServer(updatePacket); } + @Override public void updateAccounts(Account[] accounts) { Packet updatePacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), accounts); sendToLocalServer(updatePacket); } + @Override public void updateCategories(Category[] categories) { Packet updatePacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), categories); sendToLocalServer(updatePacket); } + @Override public void addNewAccount(Account account) { Packet addAccountPacket = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), account); sendToLocalServer(addAccountPacket); } + @Override public void addNewAccounts(Account[] accounts) { Packet addAccountPacket = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), accounts); sendToLocalServer(addAccountPacket); @@ -3892,9 +4001,9 @@ public void addNewAccounts(Account[] accounts) { /** * Read Configuration. - * + * * Halt if configuration is corrupt. - * + * * @param siteNum * @return true if config file read */ @@ -3904,15 +4013,15 @@ public boolean readConfigFromDisk(int siteNum) { if (saveCofigurationToDisk) { try { loadedConfiguration = contest.readConfiguration(siteNum, getLog()); - + } catch (FileNotFoundException fnf){ // This is expected loadedConfiguration = false; - + } catch (Exception e) { logException(e); - - // Bug 879 -If there is a problem reading the config then exit and show a message. + + // Bug 879 -If there is a problem reading the config then exit and show a message. fatalError("Halting server - configuration file corrupt", e); return false; } @@ -3920,36 +4029,43 @@ public boolean readConfigFromDisk(int siteNum) { return loadedConfiguration; } + @Override public void addNewClientSettings(ClientSettings clientSettings) { Packet addClientSettingsPacket = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), clientSettings); sendToLocalServer(addClientSettingsPacket); } + @Override public void updateClientSettings(ClientSettings clientSettings) { Packet updateClientSettingsPacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), clientSettings); sendToLocalServer(updateClientSettingsPacket); } + @Override public void updateContestInformation(ContestInformation contestInformation) { Packet contestInfoPacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), contestInformation); sendToLocalServer(contestInfoPacket); } + @Override public void setJudgementList(Judgement[] judgementList) { Packet updatePacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), judgementList); sendToLocalServer(updatePacket); } + @Override public void removeJudgement(Judgement judgement) { Packet deleteJudgmentPacket = PacketFactory.createDeleteSetting(contest.getClientId(), getServerClientId(), judgement); sendToLocalServer(deleteJudgmentPacket); } + @Override public void addNewBalloonSettings(BalloonSettings newBalloonSettings) { Packet newBalloonSettingsPacket = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), newBalloonSettings); sendToLocalServer(newBalloonSettingsPacket); } + @Override public void updateBalloonSettings(BalloonSettings balloonSettings) { Packet balloonSettingsPacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), balloonSettings); sendToLocalServer(balloonSettingsPacket); @@ -3959,32 +4075,37 @@ public int getSiteNumber() { return contest.getSiteNumber(); } + @Override public void updateContestTime(ContestTime newContestTime) { Packet newContestTimePacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), newContestTime); sendToLocalServer(newContestTimePacket); } + @Override public void addNewGroup(Group group) { Packet newGroupPacket = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), group); sendToLocalServer(newGroupPacket); } + @Override public void updateGroup(Group group) { Packet groupPacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), group); sendToLocalServer(groupPacket); } + @Override public int getSecurityLevel() { return securityLevel; } + @Override public void setSecurityLevel(int securityLevel) { this.securityLevel = securityLevel; } /** * Server send out security packet. - * + * */ public void sendSecurityMessageFromServer(ContestSecurityException contestSecurityException, ConnectionHandlerID connectionHandlerID, Packet packet) { Packet violationPacket = PacketFactory.createSecurityMessagePacket(contest.getClientId(), PacketFactory.ALL_SERVERS, contestSecurityException.getSecurityMessage(), null, connectionHandlerID, @@ -3997,20 +4118,24 @@ public void sendSecurityMessageFromServer(ContestSecurityException contestSecuri } + @Override public void sendSecurityMessage(String event, String message, ContestSecurityException contestSecurityException) { Packet securityMessagePacket = PacketFactory.createSecurityMessagePacket(contest.getClientId(), getServerClientId(), event, contestSecurityException.getClientId(), contestSecurityException.getConnectionHandlerID(), contestSecurityException, null); sendToLocalServer(securityMessagePacket); } + @Override public String getHostContacted() { return remoteHostName; } + @Override public int getPortContacted() { return remoteHostPort; } + @Override public void fetchRun(Run run) throws IOException, ClassNotFoundException, FileSecurityException { RunFiles runFiles = contest.getRunFiles(run); @@ -4030,42 +4155,51 @@ private void sendStatusMessge(Run run, RunExecutionStatus status) { } } + @Override public void sendCompilingMessage(Run run) { sendStatusMessge(run, RunExecutionStatus.COMPILING); } + @Override public void sendExecutingMessage(Run run) { sendStatusMessge(run, RunExecutionStatus.EXECUTING); } + @Override public void sendValidatingMessage(Run run) { sendStatusMessge(run, RunExecutionStatus.VALIDATING); } + @Override public boolean isClientAutoShutdown() { return clientAutoShutdown; } + @Override public void setClientAutoShutdown(boolean clientAutoShutdown) { this.clientAutoShutdown = clientAutoShutdown; } + @Override public void resetContest(ClientId clientResettingContest, boolean eraseProblems, boolean eraseLanguages) { Profile currentProfile = contest.getProfile(); Packet sendPacket = PacketFactory.createResetAllSitesPacket(contest.getClientId(), getServerClientId(), clientResettingContest, currentProfile, eraseProblems, eraseLanguages); sendToLocalServer(sendPacket); } + @Override public void cloneProfile(Profile profile, ProfileCloneSettings settings, boolean switchNow) { Packet sendPacket = PacketFactory.createCloneProfilePacket(contest.getClientId(), getServerClientId(), profile, settings, switchNow); sendToLocalServer(sendPacket); } + @Override public void switchProfile(Profile currentProfile, Profile switchToProfile, String contestPassword) { Packet sendPacket = PacketFactory.createSwitchProfilePacket(contest.getClientId(), getServerClientId(), currentProfile, switchToProfile, contestPassword); sendToLocalServer(sendPacket); } + @Override public void updateProfile(Profile profile) { Packet updateProfilePackert = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), profile); sendToLocalServer(updateProfilePackert); @@ -4094,12 +4228,12 @@ private void showErrorMessage(String message, String title) { /** * Fatal error - log error and show user message before exiting. - * + * * @param message * @param ex */ protected void fatalError(String message, Exception ex) { - + if (log != null) { if (ex != null) { log.log(Log.SEVERE, message, ex); @@ -4131,11 +4265,11 @@ protected void fatalError(String message, Exception ex) { if (haltOnFatalError) { System.exit(4); } - + } /** - * + * * @see #fatalError(String, Exception) * @param message */ @@ -4143,6 +4277,7 @@ private void fatalError(String message) { fatalError(message, null); } + @Override public void setContest(IInternalContest newContest) { this.contest = newContest; contest.setCommandLineArguments(parseArguments); @@ -4150,10 +4285,12 @@ public void setContest(IInternalContest newContest) { runSubmitterInterfaceManager.setContestAndController(newContest, this); } + @Override public void register(UIPlugin plugin) { pluginList.register(plugin); } + @Override public UIPlugin[] getPluginList() { return pluginList.getList(); } @@ -4170,6 +4307,7 @@ private void firePacketListener(PacketEvent packetEvent) { } } + @Override public void updateContestController(IInternalContest inContest, IInternalController inController) { setContest(inContest); @@ -4250,19 +4388,23 @@ private void logException(String message, Exception e) { } } + @Override public void addPacketListener(IPacketListener packetListener) { packetListenerList.addElement(packetListener); } + @Override public void removePacketListener(IPacketListener packetListener) { packetListenerList.removeElement(packetListener); } + @Override public void incomingPacket(Packet packet) { PacketEvent event = new PacketEvent(Action.RECEIVED, packet); firePacketListener(event); } + @Override public void outgoingPacket(Packet packet) { PacketEvent event = new PacketEvent(Action.SENT, packet); firePacketListener(event); @@ -4272,18 +4414,22 @@ public void setLog(Log log) { this.log = log; } + @Override public boolean isUsingGUI() { return usingGUI; } + @Override public boolean isSuppressConnectionsPaneDisplay() { return suppressConnectionsPaneDisplay; } + @Override public boolean isSuppressLoginsPaneDisplay() { return suppressLoginsPaneDisplay; } + @Override public ILogWindow startLogWindow(IInternalContest inContest) { if (!isUsingGUI()) { @@ -4305,11 +4451,12 @@ public ILogWindow startLogWindow(IInternalContest inContest) { return logWindow; } - private String noLogWindowAvailableMsg = - "Log Window is null or invalid; cannot show log." + private String noLogWindowAvailableMsg = + "Log Window is null or invalid; cannot show log." + "\n (Log Windows were temporarily disabled in certain Clients (see Issue https://github.com/pc2ccs/pc2v9/pull/555);" - + "\n you may be running a version of PC^2 which still includes this patch...)"; + + "\n you may be running a version of PC^2 which still includes this patch...)"; + @Override public void showLogWindow(boolean showWindow) { if (isUsingGUI()) { @@ -4322,6 +4469,7 @@ public void showLogWindow(boolean showWindow) { } } + @Override public boolean isLogWindowVisible() { if (isUsingGUI()) { if (logWindow != null && logWindow instanceof ILogWindow) { @@ -4340,6 +4488,7 @@ public void logWarning(String string) { System.err.println("Warning: " + string); } + @Override public void logWarning(String string, Exception e) { log.log(Log.WARNING, string, e); System.err.println("Warning: " + string); @@ -4367,24 +4516,28 @@ private void printStackTraceTop(Throwable throwable, PrintStream printStream, in } } + @Override public void syncProfileSubmissions(Profile profile) { Packet packet = PacketFactory.createSwitchSynchronizePacket(contest.getClientId(), getServerClientId(), profile); sendToLocalServer(packet); } + @Override public void sendShutdownAllSites() { Packet packet = PacketFactory.createShutdownAllServersPacket(contest.getClientId(), getServerClientId()); sendToLocalServer(packet); } + @Override public void sendShutdownSite(int siteNumber) { Packet packet = PacketFactory.createShutdownPacket(contest.getClientId(), getServerClientId(), siteNumber); sendToLocalServer(packet); } /** - * + * */ + @Override public void shutdownServer(ClientId requestor) { if (isServer()) { @@ -4418,6 +4571,7 @@ public void shutdownServer(ClientId requestor) { } } + @Override public void shutdownRemoteServers(ClientId requestor) { if (contest.isAllowed(requestor, Permission.Type.SHUTDOWN_ALL_SERVERS)) { @@ -4434,6 +4588,7 @@ public void shutdownRemoteServers(ClientId requestor) { } } + @Override public void shutdownServer(ClientId requestor, int siteNumber) { if (contest.isAllowed(requestor, Permission.Type.SHUTDOWN_ALL_SERVERS) || contest.isAllowed(requestor, Permission.Type.SHUTDOWN_SERVER)) { @@ -4476,30 +4631,36 @@ protected ClientId getServerClientId(int siteNumber) { return new ClientId(siteNumber, Type.SERVER, 0); } + @Override public void updateFinalizeData(FinalizeData data) { Packet updateFinalizePacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), data); sendToLocalServer(updateFinalizePacket); } + @Override public void setUsingGUI(boolean usingGUI) { this.usingGUI = usingGUI; } + @Override public void updateCategory(Category newCategory) { Packet updatePacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), newCategory); sendToLocalServer(updatePacket); } + @Override public void addNewCategory(Category newCategory) { Packet addNewCategory = PacketFactory.createAddSetting(contest.getClientId(), getServerClientId(), newCategory); sendToLocalServer(addNewCategory); } + @Override public void startPlayback(PlaybackInfo playbackInfo) { Packet startPacket = PacketFactory.createStartPlayback(contest.getClientId(), getServerClientId(), playbackInfo); sendToLocalServer(startPacket); } + @Override public void sendRunToSubmissionInterface(Run run, RunFiles runFiles) { try { runSubmitterInterfaceManager.sendRun(run, runFiles); @@ -4520,15 +4681,16 @@ public void addConsoleLogging() { System.err.println("Unable to add console logging, log is null"); } } - + public Profile getTheProfile() { return theProfile; } - + public void setTheProfile(Profile theProfile) { this.theProfile = theProfile; } - + + @Override public void autoRegister(String loginName) { ClientId serverClientId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); @@ -4541,11 +4703,11 @@ public void autoRegister(String loginName) { public void setConnectionManager(ITransportManager connectionManager) { this.connectionManager = connectionManager; } - + public void setHaltOnFatalError(boolean haltOnFatalError) { this.haltOnFatalError = haltOnFatalError; } - + public boolean isHaltOnFatalError() { return haltOnFatalError; } @@ -4573,18 +4735,19 @@ public void updateGroups(Group[] groups) { Packet updatePacket = PacketFactory.createUpdateSetting(contest.getClientId(), getServerClientId(), groups); sendToLocalServer(updatePacket); } - + /** * Creates an {@link AutoStarter} if none exists, and then instructs the AutoStarter to update its Scheduled Start Task to correspond to the Scheduled Start Time information in the * {@link ContestInformation} object in the received {@link IInternalContest}. - * + * * @param theContest * - the Contest (Model) containing the Scheduled Start Time information * @param theController * - the Controller to which this request applies */ + @Override public void updateAutoStartInformation(IInternalContest theContest, IInternalController theController) { - + // if I'm a server then if there's no AutoStarter create one, and then tell the AutoStarter to // update its Scheduled Start task list based on the ContestInformation in theContest (Model). // if (isServer()) { @@ -4641,7 +4804,7 @@ public void updateAutoStartInformation(IInternalContest theContest, IInternalCon // System.err.println ("in updateAutoStartInformation() -- NOT A SERVER!!"); // } } - + /** * Start Auto Stop Contest Clock Thread. */ @@ -4650,7 +4813,7 @@ private void updateAutoStopClockThread() { if (autoStopContestClockThread == null) { autoStopContestClockThread = new AutoStopContestClockThread(this, contest); autoStopContestClockThread.start(); - + ContestTime time = contest.getContestTime(); log.info("Started auto stop clock thread, remaining time is " + time.getRemainingTimeStr()); } @@ -4664,13 +4827,13 @@ public IInternalContest getContest() { @Override public void submitRun(ClientId submitter, Problem problem, Language language, SerializedFile mainSubmissionFile, SerializedFile[] additionalFiles, long overrideTimeMS, long overrideRunId) { - + submitRun(submitter, problem, language, null, mainSubmissionFile, additionalFiles, overrideTimeMS, overrideRunId); } @Override public void submitRun(ClientId submitter, Problem problem, Language language, String entry_point, SerializedFile mainSubmissionFile, SerializedFile[] additionalFiles, long overrideTimeMS, long overrideSubmissionId) { - + ClientId serverClientId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); Run run = new Run(submitter, language, problem); run.setEntryPoint(entry_point); @@ -4678,7 +4841,7 @@ public void submitRun(ClientId submitter, Problem problem, Language language, St Packet packet = PacketFactory.createSubmittedRun(contest.getClientId(), serverClientId, run, runFiles, overrideTimeMS, overrideSubmissionId); sendToLocalServer(packet); - + } - + } diff --git a/src/edu/csus/ecs/pc2/core/LanguageUtilities.java b/src/edu/csus/ecs/pc2/core/LanguageUtilities.java index a73f1adbe..ee02e2da6 100644 --- a/src/edu/csus/ecs/pc2/core/LanguageUtilities.java +++ b/src/edu/csus/ecs/pc2/core/LanguageUtilities.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core; import edu.csus.ecs.pc2.core.model.IInternalContest; @@ -6,13 +6,17 @@ /** * Utility methods for languages - * + * * @author Douglas A. Lane */ public class LanguageUtilities { - + /** - * List of language extensions and their full or partial language display name + * Legacy list of language extensions and their full or partial language display name + * This should be deprecated and languages defined in system.pc2.yaml should be required to + * include the CLICS ID (as shown above) and optionally a list of extensions supported by the + * language. The list below is inadequate and out of date. For the moment, it is included for + * backward compatibility. JB */ public static final String[][] LANGUAGE_LIST = { // { "java", "Java" }, // @@ -28,10 +32,9 @@ public class LanguageUtilities { { "c", "C" }, // }; - - /** + /** * get file extension. - * + * * @param filename * @return file extension if no period found returns null */ @@ -45,7 +48,7 @@ public static String getExtension(String filename) { /** * Match a contest Language for the input extension. - * + * * @param myContest contest model * @param filename base name file or full path name. * @return @@ -57,14 +60,22 @@ public static Language guessLanguage(IInternalContest myContest, String filename /** * Match a contest Language for the input extension. - * + * * @param inContest contest model * @param extension the extension without period, ex cpp * @return null if does not match any language title */ public static Language matchFirstLanguage(IInternalContest inContest, String extension) { - Language[] lang = inContest.getLanguages(); + Language[] allLangs = inContest.getLanguages(); + // first try the new way + for(Language lang : allLangs) { + if(lang.getExtensions().contains(extension)) { + return(lang); + } + } + + // if all else fails, try the old way. This should be deprecated. JB /** * Partial or complete display name */ @@ -73,7 +84,7 @@ public static Language matchFirstLanguage(IInternalContest inContest, String ext for (String[] row : LANGUAGE_LIST) { String fileExtension = row[0]; String dispName = row[1]; - + if (fileExtension.equals(extension)) { displayName = dispName; break; @@ -82,7 +93,7 @@ public static Language matchFirstLanguage(IInternalContest inContest, String ext displayName = displayName.toLowerCase(); - for (Language language : lang) { + for (Language language : allLangs) { if (language.getDisplayName().toLowerCase().indexOf(displayName) != -1) { return language; } diff --git a/src/edu/csus/ecs/pc2/core/MockController.java b/src/edu/csus/ecs/pc2/core/MockController.java index 5662325a9..69017228e 100644 --- a/src/edu/csus/ecs/pc2/core/MockController.java +++ b/src/edu/csus/ecs/pc2/core/MockController.java @@ -41,290 +41,358 @@ /** * Mock Controller. - * + * * Some data is mock data some methods implement the controller/contest. - * + * */ public class MockController implements IInternalController { private IInternalContest contest; - + private PacketHandler handler = null; - + private Log log; + @Override public void addNewAccount(Account account) { contest.addAccount(account); } + @Override public void addNewAccounts(Account[] accounts) { contest.addAccounts(accounts); } + @Override public void addNewBalloonSettings(BalloonSettings newBalloonSettings) { } + @Override public void addNewCategory(Category newCategory) { } + @Override public void addNewClientSettings(ClientSettings newClientSettings) { contest.addClientSettings(newClientSettings); } + @Override public void addNewGroup(Group group) { contest.addGroup(group); } + @Override public void addNewJudgement(Judgement judgement) { contest.addJudgement(judgement); } + @Override public void addNewLanguage(Language language) { contest.addLanguage(language); } + @Override public void addNewProblem(Problem problem, ProblemDataFiles problemDataFiles) { contest.addProblem(problem, problemDataFiles); } + @Override public void addNewProblem(Problem[] problem, ProblemDataFiles[] problemDataFiles) { for (int i = 0; i < problemDataFiles.length; i++) { contest.addProblem(problem[i], problemDataFiles[i]); } } + @Override public void addNewSite(Site site) { contest.addSite(site); } + @Override public void addPacketListener(IPacketListener packetListener) { } + @Override public void addProblem(Problem problem) { contest.addProblem(problem); } + @Override public void autoRegister(String teamInformation) { } + @Override public void cancelClarification(Clarification clarification) { } + @Override public void cancelRun(Run run) { } + @Override public void checkOutClarification(Clarification clarification, boolean readOnly) { } + @Override public void checkOutRejudgeRun(Run theRun) { } + @Override public void checkOutRun(Run run, boolean readOnly, boolean computerJudge) { } + @Override public IInternalContest clientLogin(IInternalContest internalContest, String loginName, String password) throws Exception { return null; } + @Override public void cloneProfile(Profile profile, ProfileCloneSettings settings, boolean switchNow) { } + @Override public void fetchRun(Run run) throws IOException, ClassNotFoundException, FileSecurityException { } + @Override public void forceConnectionDrop(ConnectionHandlerID connectionHandlerID) { } + @Override public void generateNewAccounts(String clientTypeName, int count, int startNumber, boolean active) { } + @Override public void generateNewAccounts(String clientTypeName, int siteNumber, int count, int startNumber, boolean active) { } + @Override public String getHostContacted() { return null; } + @Override public Log getLog() { return log; } + @Override public UIPlugin[] getPluginList() { return null; } + @Override public int getPortContacted() { return 0; } + @Override public ProblemDataFiles getProblemDataFiles(Problem problem) { return null; } + @Override public int getSecurityLevel() { return 0; } + @Override public void incomingPacket(Packet packet) { } + @Override public void initializeServer(IInternalContest contest) throws IOException, ClassNotFoundException, FileSecurityException { - + setContest(contest); } + @Override public void initializeStorage(IStorage storage) { } + @Override public boolean isClientAutoShutdown() { return false; } + @Override public boolean isLogWindowVisible() { return false; } + @Override public boolean isUsingGUI() { return false; } + @Override public void login(String loginName, String password) { } + @Override public void logoffUser(ClientId clientId) { } + @Override public void logWarning(String string, Exception e) { } + @Override public void outgoingPacket(Packet packet) { } + @Override public void register(UIPlugin plugin) { } + @Override public void removeConnection(ConnectionHandlerID connectionHandlerID) { } + @Override public void removeJudgement(Judgement judgement) { } + @Override public void removeLogin(ClientId clientId) { } + @Override public void removePacketListener(IPacketListener packetListener) { } + @Override public void requestChangePassword(String oldPassword, String newPassword) { } + @Override public void resetContest(ClientId clientResettingContest, boolean eraseProblems, boolean eraseLanguages) { } + @Override public void sendCompilingMessage(Run run) { } + @Override public void sendExecutingMessage(Run run) { } + @Override public void sendRunToSubmissionInterface(Run run, RunFiles runFiles) { } + @Override public void sendSecurityMessage(String event, String message, ContestSecurityException contestSecurityException) { } + @Override public void sendServerLoginRequest(int inSiteNumber) throws Exception { } + @Override public void sendShutdownAllSites() { } + @Override public void sendShutdownSite(int siteNumber) { } + @Override public void sendToAdministrators(Packet packet) { } + @Override public void sendToClient(Packet packet) { } + @Override public void sendToJudges(Packet packet) { } + @Override public void sendToJudgesAndOthers(Packet packet, boolean sendToServers) { } + @Override public void sendToLocalServer(Packet packet) { } + @Override public void sendToRemoteServer(int siteNumber, Packet packet) { } + @Override public void sendToScoreboards(Packet packet) { } + @Override public void sendToServers(Packet packet) { } + @Override public void sendToSpectators(Packet packet) { } + @Override public void sendToTeams(Packet packet) { } + @Override public void sendValidatingMessage(Run run) { } + @Override public void setClientAutoShutdown(boolean clientAutoShutdown) { } + @Override public void setContest(IInternalContest newContest) { this.contest = newContest; log = new Log("log", getLogName(contest.getClientId())); @@ -335,93 +403,127 @@ private String getLogName(ClientId clientId) { return "mock." + stripChar(clientId.toString(), ' '); } + @Override public void setContestTime(ContestTime contestTime) { - + } + @Override public void setJudgementList(Judgement[] judgementList) { } + @Override public void setSecurityLevel(int securityLevel) { } + @Override public void setSiteNumber(int i) { } + @Override public void setUsingGUI(boolean usingGUI) { } + @Override public void showLogWindow(boolean showWindow) { } + @Override public void shutdownRemoteServers(ClientId requestor) { } + @Override public void shutdownServer(ClientId requestor) { } + @Override public void shutdownServer(ClientId requestor, int siteNumber) { } + @Override public void shutdownTransport() { } + @Override public void start(String[] stringArray) { } + @Override public void startAllContestTimes() { } + @Override public void startContest(int inSiteNumber) { } + @Override public ILogWindow startLogWindow(IInternalContest contest) { return null; } + @Override public void startMainUI(ClientId clientId) { } + @Override public void startPlayback(PlaybackInfo playbackInfo) { } + @Override public void stopAllContestTimes() { } + @Override public void stopContest(int inSiteNumber) { } + @Override public void submitClarification(Problem problem, String question) { } + @Override public void submitClarificationAnswer(Clarification clarification) { } + @Override public void submitJudgeRun(Problem problem, Language language, String filename, SerializedFile[] otherFiles) throws Exception { submitJudgeRun(problem, language, filename, otherFiles, 0, 0); } + @Override + public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, boolean overrideStopOnFailure) throws Exception { + submitJudgeRun(problem, language, mainFileName, otherFiles, 0, 0, overrideStopOnFailure); + } + + @Override public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] auxFileList, long overrideSubmissionTimeMS, long overrideRunId) throws Exception { - + submitJudgeRun(problem, language, mainFileName, auxFileList, overrideSubmissionTimeMS, overrideRunId, false); + } + + @Override + public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, + long overrideSubmissionTimeMS, long overrideRunId, boolean overrideStopOnFailure) throws Exception { + Run submittedRun = new Run(contest.getClientId(), language, problem); + submittedRun.setOverrideStopOnFailure(overrideStopOnFailure); submittedRun.setOverRideElapsedTimeMS(overrideSubmissionTimeMS); submittedRun.setOverRideNumber(new Long(overrideRunId).intValue()); RunFiles runFiles = new RunFiles(submittedRun, mainFileName); @@ -433,120 +535,148 @@ public void submitJudgeRun(Problem problem, Language language, SerializedFile ma String mainFileName = mainFile.getName(); submitJudgeRun(problem, language, mainFileName, otherFiles, overrideSubmissionTimeMS, overrideRunId); } - + + @Override + public void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, + long overrideSubmissionTimeMS, long overrideRunId, boolean overrideStopOnFailure) throws Exception { + String mainFileName = mainFile.getName(); + submitJudgeRun(problem, language, mainFileName, otherFiles, overrideSubmissionTimeMS, overrideRunId, overrideStopOnFailure); + } + + @Override public void submitRunJudgement(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles) { } + @Override public void switchProfile(Profile currentProfile, Profile switchToProfile, String contestPassword) { } + @Override public void syncProfileSubmissions(Profile profile) { } + @Override public void updateAccount(Account account) { } + @Override public void updateAccounts(Account[] account) { } + @Override public void updateBalloonSettings(BalloonSettings newBalloonSettings) { } + @Override public void updateCategories(Category[] categories) { } + @Override public void updateCategory(Category newCategory) { } + @Override public void updateClientSettings(ClientSettings clientSettings) { } + @Override public void updateContestController(IInternalContest inContest, IInternalController inController) { } + @Override public void updateContestInformation(ContestInformation contestInformation) { } + @Override public void updateContestTime(ContestTime newContestTime) { } + @Override public void updateFinalizeData(FinalizeData data) { } + @Override public void updateGroup(Group group) { } + @Override public void updateJudgement(Judgement newJudgement) { } + @Override public void updateLanguage(Language language) { } + @Override public void updateProblem(Problem problem) { } + @Override public void updateProblem(Problem problem, ProblemDataFiles problemDataFiles) { } + @Override public void updateProfile(Profile profile) { } + @Override public void updateRun(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles) { } + @Override public void updateSite(Site newSite) { } @Override public void setConnectionManager(ITransportManager connectionManager) { - + } @Override public void addNewLanguages(Language[] languages) { - + } @Override public void updateLanguages(Language[] languages) { - + } @Override public void addNewGroups(Group[] groups) { - + } @Override public void updateGroups(Group[] groups) { - + } /** * Creates an {@link AutoStarter} if none exists, and then instructs the AutoStarter to update its Scheduled Start Task to correspond to the Scheduled Start Time information in the * {@link ContestInformation} object in the received {@link IInternalContest}. - * + * * @param theContest * - the Contest (Model) containing the Scheduled Start Time information * @param theController @@ -565,12 +695,12 @@ public IInternalContest getContest() { @Override public void submitRun(ClientId submitter, Problem problem, Language language, SerializedFile mainSubmissionFile, SerializedFile[] additionalFiles, long overrideTimeMS, long overrideRunId) { System.out.println("submitRun "+submitter+" "+problem+" "+language+" " +mainSubmissionFile.getName()+" aux file count "+additionalFiles.length+" time ="+overrideTimeMS+" run id ="+overrideRunId); - + ClientId serverClientId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); Run run = new Run(submitter, language, problem); try { - + RunFiles runFiles = new RunFiles(run, mainSubmissionFile, additionalFiles); Packet packet = PacketFactory.createSubmittedRun(contest.getClientId(), serverClientId, run, runFiles, overrideTimeMS, overrideRunId); handler.handlePacket(packet, null); @@ -579,19 +709,19 @@ public void submitRun(ClientId submitter, Problem problem, Language language, Se rethrow (e); } // sendToLocalServer(packet); - + } @Override public void submitRun(ClientId submitter, Problem problem, Language language, String entry_point, SerializedFile mainSubmissionFile, SerializedFile[] additionalFiles, long overrideTimeMS, long overrideRunId) { System.out.println("submitRun "+submitter+" "+problem+" "+language+" " +mainSubmissionFile.getName()+" aux file count "+additionalFiles.length+" entry_point ="+entry_point+" time ="+overrideTimeMS+" run id ="+overrideRunId); - + ClientId serverClientId = new ClientId(contest.getSiteNumber(), Type.SERVER, 0); Run run = new Run(submitter, language, problem); run.setEntryPoint(entry_point); - + try { - + RunFiles runFiles = new RunFiles(run, mainSubmissionFile, additionalFiles); Packet packet = PacketFactory.createSubmittedRun(contest.getClientId(), serverClientId, run, runFiles, overrideTimeMS, overrideRunId); handler.handlePacket(packet, null); @@ -604,7 +734,7 @@ public void submitRun(ClientId submitter, Problem problem, Language language, St private void rethrow(Exception e) { throw new RuntimeException(e); } - + protected String stripChar(String s, char ch) { int idx = s.indexOf(ch); while (idx > -1) { diff --git a/src/edu/csus/ecs/pc2/core/NullController.java b/src/edu/csus/ecs/pc2/core/NullController.java index 46d4050e9..9c8b3fb3c 100644 --- a/src/edu/csus/ecs/pc2/core/NullController.java +++ b/src/edu/csus/ecs/pc2/core/NullController.java @@ -39,9 +39,9 @@ /** * Null Controller, does nothing, nada, zip. - * + * * A skeleton implementation of the IInternalController. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -49,479 +49,605 @@ // $HeadURL$ public class NullController implements IInternalController { + @Override public void addNewAccount(Account account) { } + @Override public void addNewAccounts(Account[] account) { } + @Override public void addNewBalloonSettings(BalloonSettings newBalloonSettings) { } + @Override public void addNewCategory(Category newCategory) { } + @Override public void addNewClientSettings(ClientSettings newClientSettings) { } + @Override public void addNewGroup(Group group) { } + @Override public void addNewJudgement(Judgement judgement) { } + @Override public void addNewLanguage(Language language) { } + @Override public void addNewProblem(Problem problem, ProblemDataFiles problemDataFiles) { } + @Override public void addNewProblem(Problem[] problem, ProblemDataFiles[] problemDataFiles) { } + @Override public void addNewSite(Site site) { } + @Override public void addPacketListener(IPacketListener packetListener) { } + @Override public void addProblem(Problem problem) { } + @Override public void autoRegister(String teamInformation) { } + @Override public void cancelClarification(Clarification clarification) { } + @Override public void cancelRun(Run run) { } + @Override public void checkOutClarification(Clarification clarification, boolean readOnly) { } + @Override public void checkOutRejudgeRun(Run theRun) { } + @Override public void checkOutRun(Run run, boolean readOnly, boolean computerJudge) { } + @Override public IInternalContest clientLogin(IInternalContest internalContest, String loginName, String password) throws Exception { return null; } + @Override public void cloneProfile(Profile profile, ProfileCloneSettings settings, boolean switchNow) { } + @Override public void fetchRun(Run run) throws IOException, ClassNotFoundException, FileSecurityException { } + @Override public void forceConnectionDrop(ConnectionHandlerID connectionHandlerID) { } + @Override public void generateNewAccounts(String clientTypeName, int count, int startNumber, boolean active) { } + @Override public void generateNewAccounts(String clientTypeName, int siteNumber, int count, int startNumber, boolean active) { } + @Override public String getHostContacted() { return null; } + @Override public Log getLog() { return null; } + @Override public UIPlugin[] getPluginList() { return null; } + @Override public int getPortContacted() { return 0; } + @Override public ProblemDataFiles getProblemDataFiles(Problem problem) { return null; } + @Override public int getSecurityLevel() { return 0; } + @Override public void incomingPacket(Packet packet) { } + @Override public void initializeServer(IInternalContest contest) throws IOException, ClassNotFoundException, FileSecurityException { } + @Override public void initializeStorage(IStorage storage) { } + @Override public boolean isClientAutoShutdown() { return false; } + @Override public boolean isLogWindowVisible() { return false; } + @Override public boolean isUsingGUI() { return false; } + @Override public void login(String loginName, String password) { } + @Override public void logoffUser(ClientId clientId) { } + @Override public void logWarning(String string, Exception e) { } + @Override public void outgoingPacket(Packet packet) { } + @Override public void register(UIPlugin plugin) { } + @Override public void removeConnection(ConnectionHandlerID connectionHandlerID) { } + @Override public void removeJudgement(Judgement judgement) { } + @Override public void removeLogin(ClientId clientId) { } + @Override public void removePacketListener(IPacketListener packetListener) { } + @Override public void requestChangePassword(String oldPassword, String newPassword) { } + @Override public void resetContest(ClientId clientResettingContest, boolean eraseProblems, boolean eraseLanguages) { } + @Override public void sendCompilingMessage(Run run) { } + @Override public void sendExecutingMessage(Run run) { } + @Override public void sendRunToSubmissionInterface(Run run, RunFiles runFiles) { } + @Override public void sendSecurityMessage(String event, String message, ContestSecurityException contestSecurityException) { } + @Override public void sendServerLoginRequest(int inSiteNumber) throws Exception { } + @Override public void sendShutdownAllSites() { } + @Override public void sendShutdownSite(int siteNumber) { } + @Override public void sendToAdministrators(Packet packet) { } + @Override public void sendToClient(Packet packet) { } + @Override public void sendToJudges(Packet packet) { } + @Override public void sendToJudgesAndOthers(Packet packet, boolean sendToServers) { } + @Override public void sendToLocalServer(Packet packet) { } + @Override public void sendToRemoteServer(int siteNumber, Packet packet) { } + @Override public void sendToScoreboards(Packet packet) { } + @Override public void sendToServers(Packet packet) { } + @Override public void sendToSpectators(Packet packet) { } + @Override public void sendToTeams(Packet packet) { } + @Override public void sendValidatingMessage(Run run) { } + @Override public void setClientAutoShutdown(boolean clientAutoShutdown) { } + @Override public void setContest(IInternalContest newContest) { } + @Override public void setContestTime(ContestTime contestTime) { } + @Override public void setJudgementList(Judgement[] judgementList) { } + @Override public void setSecurityLevel(int securityLevel) { } + @Override public void setSiteNumber(int i) { } + @Override public void setUsingGUI(boolean usingGUI) { } + @Override public void showLogWindow(boolean showWindow) { } + @Override public void shutdownRemoteServers(ClientId requestor) { } + @Override public void shutdownServer(ClientId requestor) { } + @Override public void shutdownServer(ClientId requestor, int siteNumber) { } + @Override public void shutdownTransport() { } + @Override public void start(String[] stringArray) { } + @Override public void startAllContestTimes() { } + @Override public void startContest(int inSiteNumber) { } + @Override public ILogWindow startLogWindow(IInternalContest contest) { return null; } + @Override public void startMainUI(ClientId clientId) { } + @Override public void startPlayback(PlaybackInfo playbackInfo) { } + @Override public void stopAllContestTimes() { } + @Override public void stopContest(int inSiteNumber) { } + @Override public void submitClarification(Problem problem, String question) { } + @Override public void submitClarificationAnswer(Clarification clarification) { } + @Override public void submitJudgeRun(Problem problem, Language language, String filename, SerializedFile[] otherFiles) throws Exception { } + @Override public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] auxFileList, long overrideSubmissionTimeMS, long overrideRunId) throws Exception { } + @Override public void submitRunJudgement(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles) { } + @Override + public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, boolean overrideStopOnFailure) throws Exception { + + } + + @Override + public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, + long overrideSubmissionTimeMS, long overrideRunId, boolean overrideStopOnFailure) throws Exception { + + } + + @Override + public void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, + long overrideSubmissionTimeMS, long overrideRunId, boolean overrideStopOnFailure) throws Exception { + } + @Override public void switchProfile(Profile currentProfile, Profile switchToProfile, String contestPassword) { } + @Override public void syncProfileSubmissions(Profile profile) { } + @Override public void updateAccount(Account account) { } + @Override public void updateAccounts(Account[] account) { } + @Override public void updateBalloonSettings(BalloonSettings newBalloonSettings) { } + @Override public void updateCategories(Category[] categories) { } + @Override public void updateCategory(Category newCategory) { } + @Override public void updateClientSettings(ClientSettings clientSettings) { } + @Override public void updateContestController(IInternalContest inContest, IInternalController inController) { } + @Override public void updateContestInformation(ContestInformation contestInformation) { } + @Override public void updateContestTime(ContestTime newContestTime) { } + @Override public void updateFinalizeData(FinalizeData data) { } + @Override public void updateGroup(Group group) { } + @Override public void updateJudgement(Judgement newJudgement) { } + @Override public void updateLanguage(Language language) { } + @Override public void updateProblem(Problem problem) { } + @Override public void updateProblem(Problem problem, ProblemDataFiles problemDataFiles) { } + @Override public void updateProfile(Profile profile) { } + @Override public void updateRun(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles) { } + @Override public void updateSite(Site newSite) { } @Override public void setConnectionManager(ITransportManager connectionManager) { - + } @Override public void addNewLanguages(Language[] languages) { - + } @Override public void updateLanguages(Language[] languages) { - + } @Override public void addNewGroups(Group[] groups) { - + } @Override public void updateGroups(Group[] groups) { - + } /** * Creates an {@link AutoStarter} if none exists, and then instructs the AutoStarter to update its Scheduled Start Task to correspond to the Scheduled Start Time information in the * {@link ContestInformation} object in the received {@link IInternalContest}. - * + * * @param theContest * - the Contest (Model) containing the Scheduled Start Time information * @param theController @@ -539,17 +665,17 @@ public IInternalContest getContest() { @Override public void submitRun(ClientId submitter, Problem problem, Language language, SerializedFile mainSubmissionFile, SerializedFile[] additionalFiles, long overrideTimeMS, long overrideRunId) { - + } @Override public void submitRun(ClientId submitter, Problem problem, Language language, String entry_point, SerializedFile mainSubmissionFile, SerializedFile[] additionalFiles, long overrideTimeMS, long overrideRunId) { - + } @Override public void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, long overrideSubmissionTimeMS, long overrideRunId) throws Exception { - + } @Override diff --git a/src/edu/csus/ecs/pc2/core/PacketHandler.java b/src/edu/csus/ecs/pc2/core/PacketHandler.java index 4fe1b8840..7e0c76679 100644 --- a/src/edu/csus/ecs/pc2/core/PacketHandler.java +++ b/src/edu/csus/ecs/pc2/core/PacketHandler.java @@ -1561,6 +1561,8 @@ private void runSubmission(Packet packet, ClientId fromId, ConnectionHandlerID c } } + Boolean overrideStopOnFailure = (Boolean) PacketFactory.getObjectValue(packet, PacketFactory.OVERRIDE_STOP_ON_FAILURE); + ClientId submitter = submittedRun.getSubmitter(); boolean proxySubmission = ! submitter.equals(fromId); @@ -1589,6 +1591,7 @@ private void runSubmission(Packet packet, ClientId fromId, ConnectionHandlerID c } Run run = contest.acceptRun(submittedRun, runFiles); + boolean updateRun = false; /** * There are three conditions where a run would be added to the system but not appear on the scoreboard (or team's display): @@ -1600,6 +1603,14 @@ private void runSubmission(Packet packet, ClientId fromId, ConnectionHandlerID c if (contestTime.isPastEndOfContest() || !contestTime.isContestRunning() || submittedRun.getOverRideElapsedTimeMS() > contestTime.getContestLengthMS()) { run.setDeleted(true); submittedRun.setDeleted(true); + updateRun = true; + } + + if(overrideStopOnFailure != null && overrideStopOnFailure.booleanValue() == true) { + run.setOverrideStopOnFailure(true); + updateRun = true; + } + if(updateRun) { contest.updateRun(run, getServerClientId()); } diff --git a/src/edu/csus/ecs/pc2/core/execute/Executable.java b/src/edu/csus/ecs/pc2/core/execute/Executable.java index 630857363..9d3ef1827 100644 --- a/src/edu/csus/ecs/pc2/core/execute/Executable.java +++ b/src/edu/csus/ecs/pc2/core/execute/Executable.java @@ -510,6 +510,13 @@ public IFileViewer execute(boolean clearDirFirst) { //problem indicates stop-on-first-failure boolean stopOnFirstFailedTestCase = problem.isStopOnFirstFailedTestCase(); + // if we should override the overrideStopOnFailure, the unset our local flag. Also clear the + // run's copy of the flag since this is a one-shot flag. We do not wantto overrideStopOnFailure if + // the run is rejudged, for example. + if(run.isOverrideStopOnFailure()) { + stopOnFirstFailedTestCase = false; + run.setOverrideStopOnFailure(false); + } if (dataFiles == null || dataFiles.length <= 1) { diff --git a/src/edu/csus/ecs/pc2/core/list/StringToDoubleComparator.java b/src/edu/csus/ecs/pc2/core/list/StringToDoubleComparator.java new file mode 100644 index 000000000..8c2036d09 --- /dev/null +++ b/src/edu/csus/ecs/pc2/core/list/StringToDoubleComparator.java @@ -0,0 +1,38 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.core.list; + +import java.io.Serializable; +import java.util.Comparator; + +/** + * Compare the two strings as numbers + *

+ * Simply convert to integers and compare + * + * @version $Id$ + * @author John Buck + */ + +// $HeadURL$ +// $Id$ + +public class StringToDoubleComparator implements Comparator, Serializable { + + private static final long serialVersionUID = 1L; + + @Override + public int compare(String NumOne, String NumTwo) { + double dResult = 0; + try { + dResult = Double.parseDouble(NumOne) - Double.parseDouble(NumTwo); + } catch(Exception exception) { + return(NumOne.compareTo(NumTwo)); + } + if(dResult < 0) + return(-1); + if(dResult > 0) { + return(1); + } + return(0); + } +} diff --git a/src/edu/csus/ecs/pc2/core/model/Filter.java b/src/edu/csus/ecs/pc2/core/model/Filter.java index 3b74f3146..639e2e69a 100644 --- a/src/edu/csus/ecs/pc2/core/model/Filter.java +++ b/src/edu/csus/ecs/pc2/core/model/Filter.java @@ -2,6 +2,7 @@ package edu.csus.ecs.pc2.core.model; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -183,6 +184,9 @@ public class Filter implements Serializable { private boolean filteringGroups = false; + private HashSet customItemHash = new HashSet(); + private boolean filteringCustom = false; + /** * filtering for this site only */ @@ -1033,6 +1037,21 @@ public ClientType.Type[] getClientTypes() { return elementIds; } + /** + * Get list of custom item objects + * + * @return list of objects + */ + public ArrayList getCustomList() { + ArrayList items = new ArrayList(); + + customItemHash.forEach((custItem) -> { + items.add(custItem); + }); + + return items; + } + public void setUsingRunStatesFilter(boolean turnOn) { filteringRunStates = turnOn; } @@ -1210,6 +1229,32 @@ public void clearClarificationStateList() { clarificationStateHash = new Hashtable(); } + /** + * + * @param o Object of the string to use to attempt the lookup in the hashset + * @return true of the string is in the custom item hashset + */ + public boolean matches(Object o) { + if(filteringCustom) { + return customItemHash.contains(o.toString()); + } + return(true); + } + + /** + * + * @param o adds the string value of o to the custom item hash + */ + public void addCustomItem(Object o) { + customItemHash.add(o.toString()); + filteringCustom = true; + } + + public void clearCustomItems() { + customItemHash.clear(); + filteringCustom = false; + } + @Override public String toString() { @@ -1297,6 +1342,14 @@ public void setFilteringAccounts(boolean filteringAccounts) { this.filteringAccounts = filteringAccounts; } + public boolean isFilteringCustom() { + return filteringCustom; + } + + public void setFilteringCustom(boolean filtCustom) { + filteringCustom = filtCustom; + } + public void setFilterOff() { filterEnabled = false; } diff --git a/src/edu/csus/ecs/pc2/core/model/Language.java b/src/edu/csus/ecs/pc2/core/model/Language.java index d33455fad..d88409c3b 100644 --- a/src/edu/csus/ecs/pc2/core/model/Language.java +++ b/src/edu/csus/ecs/pc2/core/model/Language.java @@ -1,6 +1,8 @@ // Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.model; +import java.util.ArrayList; + import edu.csus.ecs.pc2.core.StringUtilities; import edu.csus.ecs.pc2.core.log.StaticLog; @@ -27,10 +29,15 @@ public class Language implements IElementObject { * ICPC contests. JB 03/20/2024 */ public static final String CLICS_LANGID_JAVA = "java"; + private static final String [] DEFAULT_EXT_JAVA = { "java" }; public static final String CLICS_LANGID_KOTLIN = "kotlin"; + private static final String [] DEFAULT_EXT_KOTLIN = { "kt" }; public static final String CLICS_LANGID_PYTHON3 = "python3"; + private static final String [] DEFAULT_EXT_PYTHON3 = { "py" }; public static final String CLICS_LANGID_C = "c"; + private static final String [] DEFAULT_EXT_C = { "c" }; public static final String CLICS_LANGID_CPP = "cpp"; + private static final String [] DEFAULT_EXT_CPP = { "cc", "cpp", "cxx", "c++" }; /** * Title for the Language. @@ -81,6 +88,8 @@ public class Language implements IElementObject { private String id = ""; + private ArrayList extensions = new ArrayList(); + public Language(String displayName) { super(); this.displayName = displayName; @@ -236,6 +245,15 @@ public int hashCode() { return elementId.hashCode(); } + /** + * Check if extensions array is allocated. If not allocate it. This can happen on deserialization of an older object. + */ + private void checkExtensions() { + if(extensions == null) { + extensions = new ArrayList(); + } + } + public String getExecutableIdentifierMask() { return executableIdentifierMask; } @@ -273,10 +291,71 @@ public boolean isUsingJudgeProgramExecuteCommandLine() { } public void setID(String newId) { - this.id = newId; + id = newId; + checkExtensions(); + // set default extensions for the language based on its CLICS id + if(extensions.isEmpty()) { + String [] ext = null; + if(newId.equals(CLICS_LANGID_C)) { + ext = DEFAULT_EXT_C; + } else if(newId.equals(CLICS_LANGID_CPP)) { + ext = DEFAULT_EXT_CPP; + } else if(newId.equals(CLICS_LANGID_PYTHON3)) { + ext = DEFAULT_EXT_PYTHON3; + } else if(newId.equals(CLICS_LANGID_JAVA)) { + ext = DEFAULT_EXT_JAVA; + } else if(newId.equals(CLICS_LANGID_KOTLIN)) { + ext = DEFAULT_EXT_KOTLIN; + } + if(ext != null) { + copyExtensions(ext); + } + } } public String getID() { return id; } + + public void setExtensions(ArrayList exts) { + checkExtensions(); + extensions.clear(); + extensions.addAll(exts); + } + + public ArrayList getExtensions() { + checkExtensions(); + if(extensions.isEmpty()) { + setDefaultExtensions(); + } + return(extensions); + } + + /** + * In the event the user did not specify a CLICS ID for the language, we have to make a guess. + * We use is the lower-case display name, minus spaces, and + changed to p, and compare to our known + * list of CLICS ids. + */ + private void setDefaultExtensions() + { + // Make lower case, get rid of spaces and convert all plus signs to p's (eg c++ -> cpp) + String fauxId = getDisplayName().toLowerCase().replaceAll("\\s", "").replaceAll("\\+", "p"); + // Let's use it as the clics ID + setID(fauxId); + // if it didn't work, then pretend we didn't try. + if(extensions.isEmpty()) { + id = null; + } + } + + /** + * Utility method to convert between a string array and ArrayList + * + * @param exts array of strings to convert + */ + private void copyExtensions(String [] exts) { + for(String ext : exts) { + extensions.add(ext); + } + } } diff --git a/src/edu/csus/ecs/pc2/core/model/Run.java b/src/edu/csus/ecs/pc2/core/model/Run.java index ebb71313d..57eff8582 100644 --- a/src/edu/csus/ecs/pc2/core/model/Run.java +++ b/src/edu/csus/ecs/pc2/core/model/Run.java @@ -111,6 +111,8 @@ public enum RunStates { private int overrideNumber = 0; + private boolean overrideStopOnFailure = false; + /** * Short/basename for submitted file. */ @@ -484,4 +486,22 @@ public String getEntryPoint(){ return null; } + + /** + * Sets the "one-shot" flag to override the stop on failure for a problem + * This is used for submitting sample judge runs. + * + * @param override true to override stop on failure, otherwise it uses the value for the problem + */ + public void setOverrideStopOnFailure(boolean override) { + overrideStopOnFailure = override; + } + + /** + * + * @return if we should override the stop on failure set for a problem + */ + public boolean isOverrideStopOnFailure() { + return overrideStopOnFailure; + } } diff --git a/src/edu/csus/ecs/pc2/core/packet/PacketFactory.java b/src/edu/csus/ecs/pc2/core/packet/PacketFactory.java index 00a5ef332..f6fd31700 100644 --- a/src/edu/csus/ecs/pc2/core/packet/PacketFactory.java +++ b/src/edu/csus/ecs/pc2/core/packet/PacketFactory.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.core.packet; import java.io.PrintStream; @@ -48,7 +48,7 @@ /** * Creates {@link Packet}s. - * + * * Each packet can be created by using a method in this class. There is a "create" method for each {@link Type}.
* There are also some methods to extract fields/classes from packets. *

@@ -56,13 +56,13 @@ *

* Constants are present in this class that are used to extract individual contents from a packet.
* Example of extracting individual contents from a packet {@link edu.csus.ecs.pc2.core.packet.Packet}. - * + * *

  * Clarification clarification = (Clarification) PacketFactory.getObjectValue(packet, PacketFactory.CLARIFICATION);
- * 
+ *
  * ClientId whoCheckedOut = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID);
  * 
- * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -96,7 +96,7 @@ public final class PacketFactory { public static final String PROBLEM = "PROBLEM"; public static final String GENERAL_PROBLEM = "GENERAL_PROBLEM"; - + public static final String GROUP = "GROUP"; public static final String CLARIFICATION_ANSWER = "CLARIFICATION_ANSWER"; @@ -110,12 +110,12 @@ public final class PacketFactory { public static final String CLIENT_ID = "CLIENT_ID"; public static final String CONTEST_PASSWORD = "CONTEST_PASSWORD"; - + /** * Array of ClientIds of logged in users. */ public static final String LOCAL_LOGGED_IN_USERS = "LOCAL_LOGGED_IN_USERS"; - + /** * Array of ClientIds of logged in users. */ @@ -160,7 +160,7 @@ public final class PacketFactory { /** * A single ClientType.Type. - * + * * @see #createGenerateAccounts(ClientId, ClientId, int, ClientType.Type, int, int, boolean) */ public static final String CLIENT_TYPE = "CLIENT_TYPE"; @@ -169,7 +169,7 @@ public final class PacketFactory { /** * Used as a start count (id) for generating accounts. - * + * * If start count is 0, will add accounts after max account number.
* If start count is 100, will add accounts after max account number is greater than 100. */ @@ -208,16 +208,16 @@ public final class PacketFactory { public static final String SITE_LIST = "SITE_LIST"; public static final String MESSAGE_STRING = "MESSAGE_STRING"; - + public static final String CONTEST_INFORMATION = "CONTEST_INFORMATION"; - + /** * Array of {@link ClientSettings}. */ public static final String CLIENT_SETTINGS_LIST = "CLIENT_SETTINGS_LIST"; - + public static final String CLIENT_SETTINGS = "CLIENT_SETTINGS"; - + /** * Array of {@link BalloonSettings}. */ @@ -234,11 +234,11 @@ public final class PacketFactory { * Boolean value. */ public static final String PASSWORD_CHANGED = "PASSWORD_CHANGED"; - + /** * On login, send settings to server. - * + * * Usually set to false.s */ public static final String SEND_SETTINGS = "SEND_SETTINGS"; @@ -274,7 +274,7 @@ public final class PacketFactory { * Array of {@link Judgement}. */ public static final String JUDGEMENT_LIST = "JUDGEMENT_LIST"; - + /** * Array of {@link ContestTime}. */ @@ -348,16 +348,18 @@ public final class PacketFactory { public static final String OVERRIDE_RUN_ID = "OVERRIDE_RUN_ID"; + public static final String OVERRIDE_STOP_ON_FAILURE = "OVERRIDE_STOP_ON_FAILURE"; + public static final String AUTO_REG_REQUEST_INFO = "AUTO_REG_REQUEST_INFO"; - + /** * List of files submitted by team. */ public static final String TEAM_RUN_SOURCE_FILES_LIST = "TEAM_RUN_SOURCE_FILES_LIST"; public static final String REQUEST_LOGIN_AS_PROXY = "REQUEST_LOGIN_AS_PROXY"; - - + + /** * Constructor is private as this is a utility class which should not be extended or invoked. */ @@ -367,7 +369,7 @@ private PacketFactory() { /** * Create a packet of {@link PacketType.Type#LOGIN_REQUEST}. - * + * * @param source - * who is logging in. * @param password - @@ -379,9 +381,9 @@ private PacketFactory() { public static Packet createLoginRequest(ClientId source, String loginName, String password, ClientId destination) { return createLoginRequest(source, loginName, password, destination, false); } - + /** - * + * * @param source * @param loginName * @param password @@ -397,7 +399,7 @@ public static Packet createLoginRequest(ClientId source, String loginName, Strin prop.put(REQUEST_LOGIN_AS_PROXY, new Boolean(proxiedSite)); return createPacket(PacketType.Type.LOGIN_REQUEST, source, destination, prop); } - + public static Packet createPasswordChangeRequest(ClientId source, ClientId destination, String password, String newPassword) { Properties prop = new Properties(); prop.put(CLIENT_ID, source); @@ -405,7 +407,7 @@ public static Packet createPasswordChangeRequest(ClientId source, ClientId dest prop.put(NEW_PASSWORD, newPassword); return createPacket(PacketType.Type.PASSWORD_CHANGE_REQUEST, source, destination, prop); } - + public static Packet createPasswordChangeResult(ClientId source, ClientId destination, boolean passwordChanged, String message) { Properties prop = new Properties(); prop.put(CLIENT_ID, source); @@ -413,11 +415,11 @@ public static Packet createPasswordChangeResult(ClientId source, ClientId desti prop.put(PacketFactory.MESSAGE_STRING, message); return createPacket(PacketType.Type.PASSWORD_CHANGE_RESULTS, source, destination, prop); } - + /** * Create a packet. - * - * + * + * * @param type - * {@link PacketType.Type} * @param source - @@ -435,7 +437,7 @@ protected static Packet createPacket(Type type, ClientId source, ClientId destin /** * Create a packet of {@link PacketType.Type#MESSAGE}. - * + * * @param source * @param destination * @param message @@ -458,12 +460,14 @@ public static Packet createMessage(ClientId source, ClientId destination, Area a } /** * Create a packet of {@link PacketType.Type#RUN_SUBMISSION}. - * + * + * This method is for backward compatibiltiy for classes that do not pass the overrideStopOnFailure flag + * * @param source * @param destination * @param run * @param runFiles - * @param overrideSubmissionTime + * @param overrideSubmissionTime * @return submitted run packet. */ public static Packet createSubmittedRun(ClientId source, ClientId destination, Run run, RunFiles runFiles, long overrideSubmissionTime, long overrideRunId) { @@ -479,19 +483,46 @@ public static Packet createSubmittedRun(ClientId source, ClientId destination, R Packet packet = new Packet(Type.RUN_SUBMISSION, source, destination, prop); return packet; } - + + /** + * Create a packet of {@link PacketType.Type#RUN_SUBMISSION}. + * + * @param source + * @param destination + * @param run + * @param runFiles + * @param overrideSubmissionTime + * @param overrideRunId + * @param overrideStopOnFailure + * @return submitted run packet. + */ + public static Packet createSubmittedRun(ClientId source, ClientId destination, Run run, RunFiles runFiles, long overrideSubmissionTime, long overrideRunId, boolean overrideStopOnFailure) { + Properties prop = new Properties(); + prop.put(RUN, run); + prop.put(RUN_FILES, runFiles); + if (overrideSubmissionTime > 0) { + prop.put(ELAPSED_TIME, new Long(overrideSubmissionTime)); + } + if (overrideRunId > 0) { + prop.put(OVERRIDE_RUN_ID, new Long(overrideRunId)); + } + prop.put(OVERRIDE_STOP_ON_FAILURE, Boolean.valueOf(overrideStopOnFailure)); + Packet packet = new Packet(Type.RUN_SUBMISSION, source, destination, prop); + return packet; + } + public static Packet createRunSubmissionConfirmation(ClientId source, ClientId destination, Run run, RunFiles runFiles) { Properties prop = new Properties(); prop.put(RUN, run); prop.put(RUN_FILES, runFiles); Packet packet = new Packet(Type.RUN_SUBMISSION_CONFIRM_SERVER, source, destination, prop); return packet; - + } /** * Dump packet info to PrintWriter and System.err. - * + * * @param pw * @param packet */ @@ -522,10 +553,10 @@ public static void dumpPacket(PrintWriter pw, Packet packet) { pw.println(); } - + /** * Dump packet info to PrintWriter and System.err. - * + * * @param log * @param packet * @param message @@ -557,10 +588,10 @@ public static void dumpPacket(Log log, Packet packet, String message) { /** * Dump packet to PrintStream. - * + * * @param pw * @param packet - * @param message + * @param message */ public static void dumpPacket(PrintStream pw, Packet packet, String message) { pw.println("Packet " + packet.getType() + " (Seq #" + packet.getPacketNumber() + ", o#" + packet.getOriginalPacketNumber() + " ) " + message); @@ -590,7 +621,7 @@ public static void dumpPacket(PrintStream pw, Packet packet, String message) { /** * Create a packet of {@link PacketType.Type#RUN_AVAILABLE}. - * + * * @param source * @param destination * @param run @@ -605,7 +636,7 @@ public static Packet createRunAvailable(ClientId source, ClientId destination, R /** * Create a packet of {@link PacketType.Type#RUN_UPDATE}. - * + * * @param source * @param destination * @param run @@ -626,7 +657,7 @@ public static Packet createRunUpdated(ClientId source, ClientId destination, Run Packet packet = new Packet(Type.RUN_UPDATE, source, destination, prop); return packet; } - + public static Packet createRunUpdateNotification(ClientId source, ClientId destination, Run run, ClientId whoModifiedRun) { Properties prop = new Properties(); prop.put(CLIENT_ID, whoModifiedRun); @@ -638,7 +669,7 @@ public static Packet createRunUpdateNotification(ClientId source, ClientId desti /** * Send checked out packet to requesting judge. - * + * * @param source * @param destination * @param run @@ -665,7 +696,7 @@ public static Packet createCheckedOutRun(ClientId source, ClientId destination, /** * Create packet that notifies judges and others that run has been checked out. - * + * * @param source * @param destination * @param run @@ -681,10 +712,10 @@ public static Packet createCheckedOutRunNotification(ClientId source, ClientId d return packet; } - + /** * Create a packet of {@link PacketType.Type#RUN_NOTAVAILABLE}. - * + * * @param source * @param destination * @param run @@ -695,10 +726,10 @@ public static Packet createRunNotAvailable(ClientId source, ClientId destination Packet packet = new Packet(Type.RUN_NOTAVAILABLE, source, destination, prop); return packet; } - + /** * Create a packet of {@link PacketType.Type#RUN_NOTAVAILABLE}. - * + * * @param source * @param destination * @param run @@ -713,7 +744,7 @@ public static Packet createRunRevoked (ClientId source, ClientId destination, Ru /** * Create a packet of {@link PacketType.Type#RUN_LIST}. - * + * * @param source * @param destination * @param runs @@ -727,7 +758,7 @@ public static Packet createRunList(ClientId source, ClientId destination, Run[] /** * Create a packet of {@link PacketType.Type#CLARIFICATION_LIST}. - * + * * @param source * @param destination * @param clarList @@ -739,7 +770,7 @@ public static Packet createClarList(ClientId source, ClientId destination, Clari /** * Create a packet of {@link PacketType.Type#RUN_UNCHECKOUT}. - * + * * @param source * @param destination * @param beingJudgingRun @@ -755,7 +786,7 @@ public static Packet createUnCheckoutRun(ClientId source, ClientId destination, /** * Create a packet of {@link PacketType.Type#RUN_JUDGEMENT}. - * + * * @param source * @param destination * @param run @@ -776,27 +807,27 @@ public static Packet createRunJudgement(ClientId source, ClientId destination, R return packet; } - + /** * Return the value (Object) inside a packet. - * + * * If the packet contents is a {@link Properties} object, will retrieve the value for the input key from that {@link Properties} * object. *

* Examples: *

      * Clarification [] clarifications =(Clarification[]) PacketFactory.getObjectValue(packet, PacketFactory.CLARIFICATION_LIST);
-     * 
+     *
      * Run [] runs = (Run[]) PacketFactory.getObjectValue(packet, PacketFactory.RUN_LIST);
-     * 
+     *
      * ContestTime contestTime = (ContestTime) PacketFactory.getObjectValue(packet, PacketFactory.CONTEST_TIME);
-     * 
+     *
      * ClientId clientId = (ClientId) PacketFactory.getObjectValue(packet, PacketFactory.CLIENT_ID);
-     * 
+     *
      * 
* One can then check for whether the item is null to determine whether item * is in the packet. - * + * * @param packet packet to extract data from. * @param key one of the many constant Strings in PacketFactory * @return a Object value for a property inside a packet, or null if object is not present. @@ -812,7 +843,7 @@ public static Object getObjectValue(Packet packet, String key) { /** * Return the String value inside a packet. - * + * * @see #getObjectValue(Packet, String) * @param packet * @param key @@ -824,7 +855,7 @@ public static String getStringValue(Packet packet, String key) { /** * Return the Boolean value inside a packet. - * + * * @see #getObjectValue(Packet, String) * @param packet * @param key @@ -836,7 +867,7 @@ public static Boolean getBooleanValue(Packet packet, String key) { /** * Create a packet of {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param language @@ -848,14 +879,14 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, Packet packet = new Packet(Type.UPDATE_SETTING, source, destination, prop); return packet; } - + public static Packet createUpdateSetting(ClientId source, ClientId destination, Profile profile) { Properties prop = new Properties(); prop.put(PROFILE, profile); Packet packet = new Packet(Type.UPDATE_SETTING, source, destination, prop); return packet; } - + public static Packet createUpdateSetting(ClientId source, ClientId destination, BalloonSettings balloonSettings) { Properties prop = new Properties(); prop.put(BALLOON_SETTINGS, balloonSettings); @@ -884,7 +915,7 @@ public static Packet createAddSetting(ClientId source, ClientId destination, Bal /** * Create a packet of {@link PacketType.Type#ADD_SETTING}. - * + * * @param source * @param destination * @param language @@ -919,7 +950,7 @@ public static Packet createAddSetting(ClientId source, ClientId destination, Pro /** * Create a packet of {@link PacketType.Type#ADD_SETTING}. - * + * * @param source * @param destination * @param judgement @@ -943,7 +974,7 @@ public static Packet createAddSetting(ClientId source, ClientId destination, Pro /** * Create a packet for a list of problems. - * + * * @param source * @param destination * @param problems @@ -957,12 +988,12 @@ public static Packet createAddSetting(ClientId source, ClientId destination, Pro return packet; } - /** + /** * Create start contest to send to server. * @param source * @param destination * @param siteNumber - * @param who + * @param who */ public static Packet createStartContestClock(ClientId source, ClientId destination, int siteNumber, ClientId who) { Properties prop = new Properties(); @@ -975,11 +1006,11 @@ public static Packet createStartContestClock(ClientId source, ClientId destinati /** * Create a stop contest to send to server. - * + * * @param source * @param destination * @param siteNumber - * @param who + * @param who */ public static Packet createStopContestClock(ClientId source, ClientId destination, int siteNumber, ClientId who) { Properties prop = new Properties(); @@ -988,10 +1019,10 @@ public static Packet createStopContestClock(ClientId source, ClientId destinatio Packet packet = new Packet(Type.STOP_CONTEST_CLOCK, source, destination, prop); return packet; } - + /** * Create a stop contest to send to clients. - * + * * @param source * @param destination * @param inSiteNumber @@ -1006,7 +1037,7 @@ public static Packet createContestStopped(ClientId source, ClientId destination, /** * Create a start contest to send to clients. - * + * * @param source * @param destination * @param inSiteNumber @@ -1025,7 +1056,7 @@ public static Packet createContestStarted(ClientId source, ClientId destination, /** * Create a packet of {@link PacketType.Type#UPDATE_CONTEST_CLOCK}. - * + * * @param source * @param destination * @param contestTime @@ -1042,7 +1073,7 @@ public static Packet createUpdateContestTime(ClientId source, ClientId destinati /** * Create a packet of {@link PacketType.Type#ACCOUNT_LOGIN}. - * + * * @param source * @param destination * @param connectionHandlerID @@ -1057,7 +1088,7 @@ public static Packet createAccountLogin(ClientId source, ClientId destination, C /** * Create a packet of {@link PacketType.Type#LOGOUT}. - * + * * @param source * @param destination * @param userId @@ -1071,7 +1102,7 @@ public static Packet createLogoff(ClientId source, ClientId destination, ClientI /** * Create a packet of {@link PacketType.Type#CLARIFICATION_SUBMISSION_CONFIRM}. - * + * * @param source * @param destination * @param newClarification @@ -1085,7 +1116,7 @@ public static Packet createClarSubmissionConfirm(ClientId source, ClientId desti /** * Create a packet of {@link PacketType.Type#CLARIFICATION_SUBMISSION}. - * + * * @param source * @param destination * @param clarification2 @@ -1100,7 +1131,7 @@ public static Packet createClarificationSubmission(ClientId source, ClientId des /** * Create a packet of {@link PacketType.Type#RUN_SUBMISSION_CONFIRM}. - * + * * @param source * @param destination * @param run @@ -1117,7 +1148,7 @@ public static Packet createRunSubmissionConfirm(ClientId source, ClientId destin * @param destination * @param connectionHandlerID * @param loggedInClientId - * @param settings + * @param settings */ public static Packet createLogin(ClientId source, ClientId destination, ConnectionHandlerID connectionHandlerID, ClientId loggedInClientId, ClientSettings settings) { Properties prop = new Properties(); @@ -1130,28 +1161,28 @@ public static Packet createLogin(ClientId source, ClientId destination, Connecti /** * Create packet for {@link PacketType.Type#LOGIN_SUCCESS}. - * + * * Sent to a client or server on successful login. This packet * has all contest data. - * + * * @param source * @param destination * @param contestTime * @param siteNumber - * @param information - * @param data + * @param information + * @param data */ public static Packet createLoginSuccess(ClientId source, ClientId destination, ContestTime contestTime, int siteNumber, ContestInformation information, ContestLoginSuccessData data) { try { Properties prop = new Properties(); - + prop.put(SITE_NUMBER, new Integer(siteNumber)); prop.put(CONTEST_TIME, contestTime); prop.put(CLIENT_ID, destination); prop.put(CONTEST_INFORMATION, information); - + addContestData(prop, data); - + TimeZone timeZone = TimeZone.getTimeZone("GMT"); GregorianCalendar gregorianCalendar = new GregorianCalendar(timeZone); prop.put(SERVER_CLOCK_OFFSET, gregorianCalendar); @@ -1165,17 +1196,17 @@ public static Packet createLoginSuccess(ClientId source, ClientId destination, C throw new SecurityException(e.getMessage()); } } - + /** * Add Contest Data into a Properties. - * + * * Can be used in a Packet. - * + * * @param prop * @param data */ private static void addContestData(Properties prop, ContestLoginSuccessData data) { - + prop.put(CONTEST_TIME_LIST, data.getContestTimes()); prop.put(PROBLEM_LIST, data.getProblems()); @@ -1199,15 +1230,15 @@ private static void addContestData(Properties prop, ContestLoginSuccessData data prop.put(SITE_NUMBER, new Integer(data.getSiteNumber())); prop.put(CONTEST_TIME, data.getContestTime()); prop.put(CONTEST_INFORMATION, data.getContestInformation()); - + if (data.getFinalizeData() != null) { prop.put(FINALIZE_DATA, data.getFinalizeData()); } - + if (data.getContestSecurityPassword() != null) { prop.put(CONTEST_PASSWORD, data.getContestSecurityPassword()); } - + if (data.getCategories() != null){ prop.put(CATEGORY_LIST, data.getCategories()); } @@ -1215,7 +1246,7 @@ private static void addContestData(Properties prop, ContestLoginSuccessData data /** * A contest settings packet. - * + * * @param source * @param destination * @param loginSuccessPacket @@ -1241,12 +1272,12 @@ public static Packet createContestSettingsPacket(ClientId source, ClientId desti } } - - + + /** * Create a packet of {@link PacketType.Type#SETTINGS}. - * + * * @param source * @param destination * @param props @@ -1258,7 +1289,7 @@ public static Packet createSettings(ClientId source, ClientId destination, Prope /** * Create packet for {@link PacketType.Type#LOGIN_FAILED}. - * + * * @param source * @param destination * @param string @@ -1273,7 +1304,7 @@ public static Packet createLoginDenied(ClientId source, ClientId destination, St /** * Create packet for {@link PacketType.Type#RUN_REQUEST}. - * + * * @param source * @param destination * @param run @@ -1289,7 +1320,7 @@ public static Packet createRunRequest(ClientId source, ClientId destination, Run Packet packet = new Packet(Type.RUN_REQUEST, source, destination, props); return packet; } - + /** * Create a request to fetch a run from the server. * @param source @@ -1305,12 +1336,12 @@ public static Packet createFetchRun(ClientId source, ClientId destination, Run r Packet packet = new Packet(Type.FETCH_RUN, source, destination, props); return packet; } - + /** * Create a run fetched (but not checked out) from server packet. - * + * * Response to createFetchRun. - * + * * @param source * @param destination * @param run @@ -1333,7 +1364,7 @@ public static Packet createFetchedRun(ClientId source, ClientId destination, Run /** * Create packet for {@link PacketType.Type#CLARIFICATION_REQUEST}. - * + * * @param source * @param destination * @param elementId @@ -1346,12 +1377,12 @@ public static Packet createClarificationRequest(ClientId source, ClientId destin Packet packet = new Packet(Type.CLARIFICATION_REQUEST, source, destination, props); return packet; } - - + + /** * Create packet for {@link PacketType.Type#CLARIFICATION_CHECKOUT}. - * + * * @param source * @param destination * @param clarification @@ -1368,7 +1399,7 @@ public static Packet createCheckedOutClarification(ClientId source, ClientId des /** * Create packet for {@link PacketType.Type#CLARIFICATION_UNCHECKOUT}. - * + * * @param source * @param destination * @param clarification @@ -1383,7 +1414,7 @@ public static Packet createUnCheckoutClarification(ClientId source, ClientId des /** * Create packet for {@link PacketType.Type#CLARIFICATION_AVAILABLE}. - * + * * @param source * @param destination * @param clarification @@ -1394,7 +1425,7 @@ public static Packet createClarificationAvailable(ClientId source, ClientId dest Packet packet = new Packet(Type.CLARIFICATION_AVAILABLE, source, destination, prop); return packet; } - + public static Packet createClarificationRevoked(ClientId source, ClientId destination, Clarification clarification, ClientId revokedFrom) { Properties prop = new Properties(); prop.put(CLARIFICATION, clarification); @@ -1406,7 +1437,7 @@ public static Packet createClarificationRevoked(ClientId source, ClientId destin /** * Create packet for {@link PacketType.Type#CLARIFICATION_UPDATE}. - * + * * @param source * @param destination * @param clarification @@ -1420,7 +1451,7 @@ public static Packet createClarificationUpdate(ClientId source, ClientId destina /** * Create packet for {@link PacketType.Type#CLARIFICATION_ANSWER}. - * + * * @param source * @param destination * @param clarification @@ -1438,7 +1469,7 @@ public static Packet createAnsweredClarification(ClientId source, ClientId desti /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param site @@ -1452,7 +1483,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#ADD_SETTING}. - * + * * @param source * @param destination * @param account @@ -1463,7 +1494,7 @@ public static Packet createAddSetting(ClientId source, ClientId destination, Acc Packet packet = new Packet(Type.ADD_SETTING, source, destination, prop); return packet; } - + public static Packet createUpdateSetting(ClientId source, ClientId destination, Account [] accounts) { Properties prop = new Properties(); prop.put(CLIENT_ID, source); @@ -1485,7 +1516,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#ADD_SETTING }. - * + * * @param source * @param destination * @param type @@ -1507,7 +1538,7 @@ public static Packet createGenerateAccounts(ClientId source, ClientId destinatio /** * Create packet for {@link PacketType.Type#ADD_SETTING }. - * + * * @param source * @param destination * @param type @@ -1523,7 +1554,7 @@ public static Packet createAddSetting(ClientId source, ClientId destination, Cli /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param account @@ -1537,7 +1568,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param group @@ -1548,10 +1579,10 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, Packet packet = new Packet(Type.UPDATE_SETTING, source, destination, prop); return packet; } - + /** * Create packet for {@link PacketType.Type#RUN_REJUDGE_REQUEST}. - * + * * @param source * @param destination * @param run @@ -1567,7 +1598,7 @@ public static Packet createRunRejudgeRequest(ClientId source, ClientId destinati /** * Create packet for {@link PacketType.Type#RUN_REJUDGE_CHECKOUT}. - * + * * @param source * @param destination * @param run @@ -1588,7 +1619,7 @@ public static Packet createRejudgeCheckedOut(ClientId source, ClientId destinati /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param timeValue @@ -1604,7 +1635,7 @@ public static Packet createUpdateContestLengthTime(ClientId source, ClientId des /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param timeValue @@ -1620,7 +1651,7 @@ public static Packet createUpdateContestRemainingTime(ClientId source, ClientId /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param timeValue @@ -1636,7 +1667,7 @@ public static Packet createUpdateContestElapsedTime(ClientId source, ClientId de /** * Create packet for {@link PacketType.Type#DROPPED_CONNECTION}. - * + * * @param source * @param destination * @param connectionHandlerID @@ -1650,7 +1681,7 @@ public static Packet createDroppedConnection(ClientId source, ClientId destinati /** * Create packet for {@link PacketType.Type#ESTABLISHED_CONNECTION}. - * + * * @param source * @param destination * @param connectionHandlerID @@ -1673,7 +1704,7 @@ public static Packet createEstablishedConnection(ClientId source, ClientId desti /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param run @@ -1690,7 +1721,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param run @@ -1714,7 +1745,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#FORCE_DISCONNECTION}. - * + * * @param source * @param destination * @param userLoginId @@ -1728,7 +1759,7 @@ public static Packet createForceLogoff(ClientId source, ClientId destination, Cl /** * Create packet for {@link PacketType.Type#FORCE_DISCONNECTION}. - * + * * @param source * @param destination * @param connectionHandlerID @@ -1742,7 +1773,7 @@ public static Packet createForceLogoff(ClientId source, ClientId destination, Co /** * Create packet for {@link PacketType.Type#START_ALL_CLOCKS}. - * + * * @param source * @param destination * @param userLoginId @@ -1756,7 +1787,7 @@ public static Packet createStartAllClocks(ClientId source, ClientId destination, // /** // * Create packet for {@link PacketType.Type#RESET_CONTEST}. -// * +// * // * @param source // * @param destination // * @param siteNumber @@ -1773,7 +1804,7 @@ public static Packet createStartAllClocks(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#RESET_ALL_CONTESTS}. - * + * * @param source * @param destination * @param userLoginId @@ -1787,7 +1818,7 @@ public static Packet createResetAllSites(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param languageDisplayList @@ -1804,7 +1835,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param problemDisplayList @@ -1821,7 +1852,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param answer @@ -1838,7 +1869,7 @@ public static Packet createUpdateSettingDefaultClarificationAnswer(ClientId sour /** * Create packet for {@link PacketType.Type#STOP_ALL_CLOCKS}. - * + * * @param source * @param destination * @param userLoginId @@ -1861,7 +1892,7 @@ public static Packet createStopAllClocks(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param clarification @@ -1887,7 +1918,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#UPDATE_SETTING}. - * + * * @param source * @param destination * @param clarification @@ -1908,7 +1939,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, /** * Create packet for {@link PacketType.Type#CONTEST_TIME}. - * + * * @param source * @param destination * @param inContestTime @@ -1927,26 +1958,26 @@ public static Packet createAddSetting(ClientId source, ClientId destination, Acc prop.put(ACCOUNT_ARRAY, accounts); return createPacket(PacketType.Type.ADD_SETTING, source, destination, prop); } - + public static Packet createAddSetting(ClientId source, ClientId destination, ClientSettings clientSettings) { Properties prop = new Properties(); prop.put(CLIENT_SETTINGS, clientSettings); Packet packet = new Packet(Type.ADD_SETTING, source, destination, prop); return packet; } - + public static Packet createRunJudgmentUpdate(ClientId source, ClientId destination, Run run, ClientId whoJudgedId) { Properties prop = new Properties(); prop.put(CLIENT_ID, whoJudgedId); prop.put(RUN, run); return createPacket(PacketType.Type.RUN_JUDGEMENT_UPDATE, source, destination, prop); } - + /** * Clone packet from existing packet. - * + * * Can reassign source and destination with affecting contents. - * + * * @param source * @param destination * @param packet @@ -1958,12 +1989,12 @@ public static Packet clonePacket(ClientId source, ClientId destination, Packet p newPacket.setOriginalSourceId(packet.getOriginalSourceId()); return newPacket; } - + /** * Clone packet and change packet type. - * + * * Can reassign type, source, and destination with affecting contents. - * + * * @param type * @param source * @param destination @@ -2013,7 +2044,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, Packet packet = new Packet(Type.UPDATE_SETTING, source, destination, prop); return packet; } - + public static Packet createUpdateSetting(ClientId source, ClientId destination, Judgement[] judgements) { Properties prop = new Properties(); prop.put(JUDGEMENT_LIST, judgements); @@ -2050,7 +2081,7 @@ public static Packet createReconnectPacket(ClientId source, ClientId destination return packet; } - public static Packet createSecurityMessagePacket(ClientId source, ClientId destination, String message, ClientId whoCanceledRun, + public static Packet createSecurityMessagePacket(ClientId source, ClientId destination, String message, ClientId whoCanceledRun, ConnectionHandlerID connectionHandlerID, ContestSecurityException contestSecurityException, Packet inPacket) { Properties prop = new Properties(); if (whoCanceledRun != null){ @@ -2061,12 +2092,12 @@ public static Packet createSecurityMessagePacket(ClientId source, ClientId desti prop.put(CONNECTION_HANDLE_ID, connectionHandlerID); } if (inPacket != null){ - prop.put(PACKET, inPacket); + prop.put(PACKET, inPacket); } if (contestSecurityException != null){ prop.put(EXCEPTION, contestSecurityException); } - + Packet packet = new Packet(Type.SECURITY_MESSAGE, source, destination, prop); return packet; } @@ -2086,9 +2117,9 @@ public static Packet createResetAllSitesPacket(ClientId source, ClientId destina prop.put(DELETE_LANGUAGE_DEFINITIONS, new Boolean(eraseLanguages)); prop.put(PROFILE, newProfile); return createPacket(PacketType.Type.RESET_ALL_CONTESTS, source, destination, prop); - + } - + public static Packet createCloneProfilePacket(ClientId source, ClientId destination, Profile profile2, ProfileCloneSettings settings, boolean switchNow) { Properties prop = new Properties(); prop.put(CLIENT_ID, source); @@ -2106,24 +2137,24 @@ public static Packet createSwitchProfilePacket(ClientId source, ClientId destina prop.put(CONTEST_PASSWORD, contestPassword); return createPacket(PacketType.Type.SWITCH_PROFILE, source, destination, prop); } - + // TODO fold contestTime and ContestInformation into ContestLoginSuccessData public static Packet createUpdateProfileClientPacket(ClientId source, ClientId destination, Profile currentProfile, Profile switchToProfile, ContestLoginSuccessData data) { try { - + Properties prop = new Properties(); - + prop.put(PROFILE, currentProfile); prop.put(NEW_PROFILE, switchToProfile); - + prop.put(CLIENT_ID, destination); - prop.put(PROFILE_LIST, data.getProfiles()); + prop.put(PROFILE_LIST, data.getProfiles()); if (data.getContestSecurityPassword() != null) { prop.put(CONTEST_PASSWORD, data.getContestSecurityPassword()); } - + addContestData(prop, data); TimeZone timeZone = TimeZone.getTimeZone("GMT"); @@ -2136,7 +2167,7 @@ public static Packet createUpdateProfileClientPacket(ClientId source, ClientId d Packet packet = new Packet(Type.UPDATE_CLIENT_PROFILE, source, destination, prop); return packet; - + } catch (Exception e) { System.err.println("Exception creating UPDATE_CLIENT_PROFILE"); e.printStackTrace(System.err); @@ -2152,7 +2183,7 @@ public static Packet createFetchRunFilesPacket(ClientId source, ClientId destina Packet packet = new Packet(Type.FETCH_RUN_FILES, source, destination, prop); return packet; } - + public static Packet createRequestRemoteDataPacket(ClientId source, ClientId destination) { Properties prop = new Properties(); prop.put(CLIENT_ID, source); @@ -2167,7 +2198,7 @@ public static Packet createRunFilesPacket(ClientId source, ClientId destination, Packet packet = new Packet(Type.UPDATE_RUN_FILES, source, destination, prop); return packet; } - + public static Packet createRequestServerStatusPacket(ClientId source, ClientId destination, Profile currentProfile) { Properties prop = new Properties(); prop.put(CLIENT_ID, source); @@ -2240,7 +2271,7 @@ public static Packet createStartPlayback(ClientId source, ClientId destination, Packet packet = new Packet(Type.START_PLAYBACK, source, destination, prop); return packet; } - + public static Packet createUpdateSetting(ClientId source, ClientId destination, PlaybackInfo playbackInfo) { Properties prop = new Properties(); prop.put(CLIENT_ID, source); @@ -2296,10 +2327,10 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, Packet packet = new Packet(Type.UPDATE_SETTING, source, destination, prop); return packet; } - + /** * List of team source files. - * + * *

* This is a response/reply to a {@link #createRunSourceFetchRequest(ClientId, ClientId, Run[])}. */ @@ -2310,7 +2341,7 @@ public static Packet createUpdateSetting(ClientId source, ClientId destination, Packet packet = new Packet(Type.UPDATE_SETTING, source, destination, prop); return packet; } - + /** * Request a list of team source files. */ @@ -2321,5 +2352,5 @@ public static Packet createRunSourceFetchRequest(ClientId source, ClientId desti Packet packet = new Packet(Type.REQUEST_FETCH_TEAMS_SUBMISSION_FILES, source, destination, prop); return packet; } - + } diff --git a/src/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoader.java b/src/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoader.java index eb1ddb349..51553af79 100644 --- a/src/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoader.java +++ b/src/edu/csus/ecs/pc2/imports/ccs/ContestSnakeYAMLLoader.java @@ -2018,11 +2018,15 @@ public Language[] getLanguages(String[] yamlLines) { language.setUsingJudgeProgramExecuteCommandLine(false); } - String clicsLanguageId = fetchValue(map, "clics-id"); + String clicsLanguageId = fetchValue(map, CLICS_LANG_ID); if (clicsLanguageId != null){ language.setID(clicsLanguageId); } + Object exts = fetchObjectValue(map, LANG_EXTENSIONS); + if(exts != null && exts instanceof ArrayList) { + language.setExtensions((ArrayList)exts); + } // SOMEDAY handle interpreted languages, seems it should be in the export if (valid(language, name)) { diff --git a/src/edu/csus/ecs/pc2/imports/ccs/IContestLoader.java b/src/edu/csus/ecs/pc2/imports/ccs/IContestLoader.java index ed07ac4af..2cc905732 100644 --- a/src/edu/csus/ecs/pc2/imports/ccs/IContestLoader.java +++ b/src/edu/csus/ecs/pc2/imports/ccs/IContestLoader.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.imports.ccs; import java.io.File; @@ -16,9 +16,9 @@ /** * Contest Loader interface and Constants. - * + * * Constants and methods used in loading YAML into contest/model. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public interface IContestLoader { @@ -28,21 +28,21 @@ public interface IContestLoader { String DEFAULT_PROBLEM_YAML_FILENAME = "problem.yaml"; String DEFAULT_PROBLEM_SET_YAML_FILENAME = "problemset.yaml"; - + String DEFAULT_PROBLEM_LATEX_FILENAME = "problem.tex"; - + String DEFAULT_ENGLISH_PROBLEM_LATEX_FILENAME = "problem.en.tex"; String DEFAULT_SYSTEM_YAML_FILENAME = "system.yaml"; - + // CDP directories - + String SUBMISSIONS_DIRNAME = "submissions"; - + String CONFIG_DIRNAME = "config"; // Sandbox fields - + final String SANDBOX_PROGRAM_NAME_KEY = "sandbox-program-name"; final String SANDBOX_COMMAND_LINE_KEY = "sandbox-command-line"; @@ -70,33 +70,33 @@ public interface IContestLoader { // reading it from properties. It is here for completeness, and, it happens // to be a required value in the yaml, but we do not enforce that. final String CLICS_CONTEST_PENALTY_TIME = "penalty_time"; - + // Section Names - + String CONTEST_NAME_KEY = "name"; String SHORT_NAME_KEY = "short-name"; String CONTEST_START_TIME_KEY = "start-time"; - + String MAX_OUTPUT_SIZE_K_KEY = "max-output-size-K"; - + String CLICS_MAX_OUTPUT_KEY = "output"; - + String CLICS_TIME_MULTIPLIER_KEY = "time_multiplier"; - + String CLICS_TIME_SAFETY_MARGIN_KEY = "time_safety_margin"; - + final String OUTPUT_PRIVATE_SCORE_DIR_KEY = "output-private-score-dir"; final String OUTPUT_PUBLIC_SCORE_DIR_KEY = "output-public-score-dir"; String CONTEST_DURATION_KEY = "duration"; - + String AUTO_STOP_CLOCK_AT_END_KEY = "auto-stop-clock-at-end"; String SCOREBOARD_FREEZE_KEY = "scoreboard-freeze"; - + String SCOREBOARD_FREEZE_LENGTH_KEY = "scoreboard-freeze-length"; String LANGUAGE_KEY = "languages"; @@ -109,14 +109,14 @@ public interface IContestLoader { * Name for problem set in contest.yaml */ String PROBLEMS_KEY = "problemset"; - + /** * name for problem set in problemset.yaml */ String PROBLEMSET_PROBLEMS_KEY = "problems"; String MANUAL_REVIEW_KEY = "manual-review"; - + String COMPUTER_JUDGING_KEY = "computer-judged"; String ACCOUNTS_KEY = "accounts"; @@ -128,11 +128,11 @@ public interface IContestLoader { String JUDGE_CONFIG_PATH_KEY = "judge-config-path"; String TIMEOUT_KEY = "timeout"; - + final String MEMORY_LIMIT_IN_MEG_KEY = "memory-limit-in-Meg"; - + final String MEMORY_LIMIT_CLICS = "memory"; - + String LIMITS_KEY = "limits"; String PROBLEM_NAME_KEY = "title"; @@ -142,13 +142,13 @@ public interface IContestLoader { String AUTO_JUDGE_KEY = "auto-judging"; String CCS_TEST_MODE = "ccs-test-mode"; - + String LOAD_SAMPLE_JUDGES_DATA = "load-sample-judges-data"; - + String INPUT_KEY = "input"; String PROBLEM_LOAD_DATA_FILES_KEY = "load-data-files"; - + String GROUPS_KEY = "groups"; String DEFAULT_VALIDATOR_KEY = "default-validator"; @@ -156,13 +156,13 @@ public interface IContestLoader { String OVERRIDE_VALIDATOR_KEY = "override-validator"; String VALIDATOR_KEY = "validator"; - + String VALIDATOR_FLAGS_KEY = "validator_flags"; String USING_PC2_VALIDATOR = "use-internal-validator"; String SEND_PRELIMINARY_JUDGEMENT_KEY = "send-prelim-judgement"; - + String STOP_ON_FIRST_FAILED_TEST_CASE_KEY = "stop-on-first-failed-test-case"; String USE_JUDGE_COMMAND_KEY = "use-judge-cmd"; @@ -181,12 +181,16 @@ public interface IContestLoader { String READ_FROM_STDIN_KEY = "readFromSTDIN"; + String CLICS_LANG_ID = "clics-id"; + + String LANG_EXTENSIONS = "extensions"; + /** * output validators directory name. */ final String OUTPUT_VALIDATORS = "output_validators"; - + //keys for YAML entries specifying data for Input Validators: String INPUT_VALIDATOR_KEY = "input_validator"; //the section header for Input Validator info String DEFAULT_INPUT_VALIDATOR_KEY = "defaultInputValidator"; //the key for specifying the type of Input Validator selected by default @@ -197,7 +201,7 @@ public interface IContestLoader { // per problem problem.yaml settings String VALIDATION_TYPE = "validation"; - + String SHOW_OUTPUT_WINDOW = "showOutputWindow"; String SHOW_COMPARE_WINDOW = "showCompare"; @@ -209,13 +213,13 @@ public interface IContestLoader { String USING_CLICS_VALIDATOR = "use-clics-validator"; String USING_CUSTOM_VALIDATOR = "use-custom-validator"; - + String USE_CLICS_CUSTOM_VALIDATOR_INTERFACE = "use-clics-custom-validator-interface"; - + String PC2_EXEC_CMD = "execCmd"; - + String PC2_COMPILER_CMD = "compilerCmd"; - + String CCS_LAST_EVENT_ID_KEY = "ccs-last-event-id"; String CCS_PASSWORD_KEY = "ccs-password"; @@ -225,13 +229,13 @@ public interface IContestLoader { String CCS_URL_KEY = "ccs-url"; String SHADOW_MODE_KEY = "shadow-mode"; - + String ALLOW_MULTIPLE_TEAM_LOGINS_KEY = "allow-multiple-team-logins"; - + String LOAD_ACCOUNTS_FILE_KEY = "load-accounts-file"; /** - * + * * @see ScoreboardVariableReplacer#substituteDisplayNameVariables(String, IInternalContest, edu.csus.ecs.pc2.core.model.Account) */ String TEAM_SCOREBOARD_DISPLAY_FORMAT_STRING = "team-scoreboard-display-format-string"; @@ -242,18 +246,18 @@ public interface IContestLoader { /** * Load contest data from contest.yaml. - * + * * @param contest * @param directoryName * directory to load files from. * @return contest - * + * */ IInternalContest fromYaml(IInternalContest contest, String directoryName); /** * Load contest, optionally load problem data files. - * + * * @param contest * @param directoryName * directory to load files from. @@ -303,20 +307,20 @@ Problem[] getProblems(String[] yamlLines, int seconds, long maxOutputSizeInBytes /** * Are data file contents to ba loaded into configuration?. - * + * * @return true if data files are not external */ boolean isLoadProblemDataFiles(); /** * Load CCS data files, and validator into contest. - * + * * @param contest * @param dataFileBaseDirectory * the directory where the .in and .ans files are found. * @param problem * @param problemDataFiles - * + * */ Problem loadCCSProblemFiles(IInternalContest contest, String dataFileBaseDirectory, Problem problem, ProblemDataFiles problemDataFiles); @@ -337,7 +341,7 @@ Problem[] getProblems(String[] yamlLines, int seconds, long maxOutputSizeInBytes /** * Directory for judge's input/answer files. - * + * * @param yamlDirectory * directory where problem dir/files are located. * @param problem @@ -347,7 +351,7 @@ Problem[] getProblems(String[] yamlLines, int seconds, long maxOutputSizeInBytes /** * Return directory for judge's input/answer files. - * + * * @param yamlDirectory * directory where problem dir/files are located. * @param shortDirName @@ -363,20 +367,20 @@ Problem[] getProblems(String[] yamlLines, int seconds, long maxOutputSizeInBytes * Load contest with settings from file/CDP. * @param contest * @param entry can be CDP directory, contest.yaml file, or other initialize file. - * @throws Exception + * @throws Exception */ IInternalContest initializeContest (IInternalContest contest, File entry) throws Exception; /** * Locates the CDP config directory. - * + * * Attempts to find CDP config directory at: *

  • current directory *
  • parent directory *
  • samps/contests/ directory, ex "mini" finds samps/contests//config - * + * * If no directory found, returns null. - * + * * @param entry * a directory, filename or PC^2 sample CCS contest directory name. * @return null or the location of the CDP config/ directory. diff --git a/src/edu/csus/ecs/pc2/list/ListUtilities.java b/src/edu/csus/ecs/pc2/list/ListUtilities.java new file mode 100644 index 000000000..1b22da552 --- /dev/null +++ b/src/edu/csus/ecs/pc2/list/ListUtilities.java @@ -0,0 +1,224 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.list; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import edu.csus.ecs.pc2.core.LanguageUtilities; +import edu.csus.ecs.pc2.core.model.IInternalContest; +import edu.csus.ecs.pc2.core.model.Language; +import edu.csus.ecs.pc2.core.model.Problem; +import edu.csus.ecs.pc2.imports.ccs.IContestLoader; +import edu.csus.ecs.pc2.validator.clicsValidator.ClicsValidator; + +/** + * A set of utilities that operate on lists. + * + * @author Douglas A. Lane + * + */ +public class ListUtilities { + + /** + * Returns list of files that match the SubmissionSolutionList (judging types location/list) + * + * @param files list of files to be filtered + * @param submissionSolutionList List of types to accept + * @return + */ + public static List filterByJudgingTypes(List files, List submissionSolutionList) { + + List newFileList = new ArrayList(); + int idx; + + for (File file : files) { + String filePath = file.getAbsolutePath(); + for(String subSol : submissionSolutionList) { + idx = subSol.indexOf(':'); + if(idx > 0) { + subSol = subSol.substring(0, idx).trim(); + } + if (filePath.contains(File.separator + subSol + File.separator)) { + newFileList.add(file); + } + } + } + + return newFileList; + } + + /** + * Returns list of all files under dir path. + * + * @param dirName location to fetch files from + * @return list of files in and under dirName + */ + // TODO REFACTOR replace findAll from QuickSubmitter with this method. + public static List findAllFiles(String dirName) { + + List files = new ArrayList<>(); + + File dir = new File(dirName); + File[] entries = dir.listFiles(); + + if (entries == null) { + return files; + } + + for (File f : entries) { + if (f.isDirectory()) { + List subList = findAllFiles(f.getAbsolutePath()); + files.addAll(subList); + } else { + files.add(f); + } + } + + return files; + } + + /** + * Get all CDP judge's sample submission filenames. + * + * Example files under config\sumit\submissions + * + * @param mycontest + * @param directoryName CDP config/ dir or CDP base directory + * @return list of judges sample submissions + */ + // TODO REFACTOR remove getAllCDPsubmissionFileNames from QuickSubmitter + public static List getAllJudgeSampleSubmissionFilenamesFromCDP(IInternalContest mycontest, String directoryName) { + + Problem[] problems = mycontest.getProblems(); + List files = new ArrayList<>(); + + String configDir = directoryName + File.separator + IContestLoader.CONFIG_DIRNAME; + if (new File(configDir).isDirectory()) { + directoryName = configDir; + } + + for (Problem problem : problems) { + + // config\sumit\submissions\accepted\ISumit.java + String probSubmissionDir = directoryName + File.separator + problem.getShortName() + File.separator + + IContestLoader.SUBMISSIONS_DIRNAME; + files.addAll(findAllFiles(probSubmissionDir)); + + } + + HashSet allExts = new HashSet(); + String ext, srcFile; + int ridx; + + // make up hashset of all language extensions + for(Language lang : mycontest.getLanguages()) { + allExts.addAll(lang.getExtensions()); + } + // Reverse scan for uninteresting files and remove them. + for(int i = files.size(); --i >= 0; ) { + srcFile = files.get(i).getName(); + ridx = srcFile.lastIndexOf('.'); + // if no extension on the file, or, it's not in our list of src extensions, drop the file. + if(ridx == -1 || !allExts.contains(srcFile.substring(ridx+1))) { + files.remove(i); + } + } + + return files; + } + + /** + * Gets a list of all the different types of judges' submissions by scanning the submissions folder for + * each problem. ex. accepted, wrong_answer, time_limit_exceeded, other, run_time_exception, etc. + * To qualify as a valid type, the particular folder must contain at least one source file with a known + * extension for the configurationed languages. + * + * @param mycontest The contest + * @param directoryName Where to start looking (config folder) + * @return List of folder basenames + */ + public static List getAllCDPSubmissionTypes(IInternalContest mycontest, String directoryName) { + List files = getAllJudgeSampleSubmissionFilenamesFromCDP(mycontest, directoryName); + String srcName; + int idx; + ArrayList types = new ArrayList(); + HashSet uniqTypes = new HashSet(); + + for(File src : files ) { + srcName = src.getParent(); + idx = srcName.lastIndexOf(File.separator); + if(idx != -1) { + srcName = srcName.substring(idx+1); + } + if(!uniqTypes.contains(srcName)) { + types.add(srcName); + uniqTypes.add(srcName); + } + } + types.sort(null); + return(types); + } + + /** + * Find files (in CDP) that contain one of the Problems in the list. + * + */ + public static List filterByProblems(List files, List selectedProblemList) { + + List newFileList = new ArrayList(); + + for (File file : files) { + selectedProblemList.forEach((prob) -> { + if (file.getAbsolutePath().contains(File.separator + prob.getShortName() + File.separator)) { + newFileList.add(file); + } + }); + } + + return newFileList; + + } + + /** + * Find all accepted/Yes filenames with "accepted" in file list. + * + * @param files list of files + * @return list of files that have a directory name of "accepted" embedded in their path. + */ + public static List filterYesJudgingType (List files) { + + List newFileList = new ArrayList(); + + for (File file : files) { + if (file.getAbsolutePath().contains(File.separator + ClicsValidator.CLICS_CORRECT_ANSWER_MSG + File.separator)) { + newFileList.add(file); + } + } + + return newFileList; + } + + public static List filterByLanguages(List files, IInternalContest contest, List selectedLanguageList) { + + List newFileList = new ArrayList(); + + for (File file : files) { + Language language = LanguageUtilities.guessLanguage(contest, file.getName()); + + if(language != null) { + selectedLanguageList.forEach((lang) -> { + + // Should use clics_id, but, I suppose it may not be there - is it required? -- JB + if (lang.getDisplayName().contentEquals(language.getDisplayName())) { + newFileList.add(file); + } + }); + } + } + + return newFileList; + + } +} diff --git a/src/edu/csus/ecs/pc2/list/SubmissionSample.java b/src/edu/csus/ecs/pc2/list/SubmissionSample.java new file mode 100644 index 000000000..77cd40f02 --- /dev/null +++ b/src/edu/csus/ecs/pc2/list/SubmissionSample.java @@ -0,0 +1,84 @@ +// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.list; + +/** + * A judge's submission sample + * + * @author John Buck, PC^2 Team, pc2@ecs.csus.edu + */ +import java.io.File; + +import edu.csus.ecs.pc2.core.model.ElementId; +import edu.csus.ecs.pc2.core.model.Run; + +public class SubmissionSample { + + private ElementId elementId = null; + private String problemName = null; + private ElementId problem = null; + + private String languageName = null; + private ElementId language = null; + + private String sampleType = null; + + private File srcFile = null; + + private Run run = null; + + public SubmissionSample(String probShortName, ElementId prob, String langName, ElementId lang, String type, File srcFile) { + elementId = new ElementId(getClass().getName()); + problemName = probShortName; + problem = prob; + languageName = langName; + language = lang; + sampleType = type; + this.srcFile = srcFile; + } + + public ElementId getElementId() { + return(elementId); + } + + public void setRun(Run r) { + run = r; + } + + public Run getRun() { + return run; + } + + public String getProblemName() { + return problemName; + } + + public ElementId getProblem() { + return problem; + } + + public String getLanguageName() { + return languageName; + } + + public ElementId getLanguage() { + return language; + } + + public String getSampleType() { + return sampleType; + } + + public File getSourceFile() { + return srcFile; + } + + @Override + public String toString() { + String str = getProblemName() + " (" + getSampleType() + ") Language: " + getLanguageName() + ": " + getSourceFile().getAbsolutePath(); + Run run = getRun(); + if(run != null) { + str = str + " (RunId " + run.getNumber() + ")"; + } + return(str); + } +} diff --git a/src/edu/csus/ecs/pc2/list/SubmissionSampleLocation.java b/src/edu/csus/ecs/pc2/list/SubmissionSampleLocation.java new file mode 100644 index 000000000..87c3d48bb --- /dev/null +++ b/src/edu/csus/ecs/pc2/list/SubmissionSampleLocation.java @@ -0,0 +1,81 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.list; + +import edu.csus.ecs.pc2.clics.CLICSJudgementType; + +/** + * A single entry of a judge's solution locations. + * + * @author Douglas A. Lane + */ +public class SubmissionSampleLocation implements Comparable { + // These are the folders defined by the clics spec for the location of judge's submissions + // (Except for compile error - that is not in the clics spec) + public static final String CLICS_SUBMISSION_LOCATION_AC = "accepted"; + public static final String CLICS_SUBMISSION_LOCATION_WA = "wrong_answer"; + public static final String CLICS_SUBMISSION_LOCATION_RTE = "run_time_error"; + public static final String CLICS_SUBMISSION_LOCATION_TLE = "time_limit_exceeded"; + public static final String CLICS_SUBMISSION_LOCATION_CE = "compile_error"; + + public static final String [][] CLICS_SUBMISSION_TO_ACRONYM = { + { CLICS_SUBMISSION_LOCATION_AC, CLICSJudgementType.CLICS_BIG5.AC.toString() }, + { CLICS_SUBMISSION_LOCATION_WA, CLICSJudgementType.CLICS_BIG5.WA.toString() }, + { CLICS_SUBMISSION_LOCATION_RTE, CLICSJudgementType.CLICS_BIG5.RTE.toString() }, + { CLICS_SUBMISSION_LOCATION_TLE, CLICSJudgementType.CLICS_BIG5.TLE.toString() }, + { CLICS_SUBMISSION_LOCATION_CE, CLICSJudgementType.CLICS_BIG5.CE.toString() } + }; + + private String title; + private String shortDirectoryName; + private String clicsAcronym; + + /** + * A judges solution name and location. + * + * @param title title for directory, ex. Run Time Error + * @param shortDirectoryName base directory name, ex. run_time_error + * @param clicsAcronym Big-5 acronym AC/WA/RTE/TLE/CE + */ + public SubmissionSampleLocation(String title, String shortDirectoryName, String clicsAcronym) { + this.title = title; + this.shortDirectoryName = shortDirectoryName; + if(clicsAcronym == null || clicsAcronym.isEmpty()) { + this.clicsAcronym = "NA"; + } else { + this.clicsAcronym = clicsAcronym; + } + } + + public String getTitle() { + return title; + } + + public String getShortDirectoryName() { + return shortDirectoryName; + } + + public String getCLICSAcronym() { + return clicsAcronym; + } + + @Override + public String toString() { + String strVal; + + if (title.length() > 0) { + strVal = title + " ("+shortDirectoryName+")"; + } else { + strVal = shortDirectoryName; + } + if(!clicsAcronym.isEmpty()) { + strVal = strVal + " : " + clicsAcronym; + } + return strVal; + } + + @Override + public int compareTo(SubmissionSampleLocation o) { + return o.toString().compareTo(toString()); + } + +} diff --git a/src/edu/csus/ecs/pc2/list/SubmissionSolutionList.java b/src/edu/csus/ecs/pc2/list/SubmissionSolutionList.java new file mode 100644 index 000000000..52efbe81e --- /dev/null +++ b/src/edu/csus/ecs/pc2/list/SubmissionSolutionList.java @@ -0,0 +1,99 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.list; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +import edu.csus.ecs.pc2.core.model.IInternalContest; + +/** + * List of judge's solutions + * + * @author Douglas A. Lane + * + */ +public class SubmissionSolutionList extends ArrayList { + + /** + * + */ + private static final long serialVersionUID = 6231245379952020474L; + + private Hashtable submissionDirectoryToAcronym = new Hashtable(); + + public SubmissionSolutionList() { + super(); + createSubmissionDirectoryMap(); + } + + public SubmissionSolutionList(IInternalContest mycontest, String cdpPath) { + super(); + createSubmissionDirectoryMap(); + loadCDPSampleSubmissionList(mycontest, cdpPath); + } + + private void createSubmissionDirectoryMap() { + // Create quick access to look up acronym for folder + for(String [] subFolderRow : SubmissionSampleLocation.CLICS_SUBMISSION_TO_ACRONYM) { + submissionDirectoryToAcronym.put(subFolderRow[0], subFolderRow[1]); + } + } + + /** + * get all directories and child directories (recurses). + * + * @param directory + * @return list of directory names + */ + // TODO REFACTOR move to FileUtilities.getAllDirectoryEntries + public static List getAllDirectoryEntries(String directory) { + + ArrayList list = new ArrayList<>(); + + File[] files = new File(directory).listFiles(); + + if (files != null) { + + for (File entry : files) { + if (entry.isDirectory()) { + list.add(directory + File.separator + entry.getName()); + if (!(entry.getName().equals(".") || entry.getName().equals(".."))) { + list.addAll(getAllDirectoryEntries(directory + File.separator + entry.getName())); + } + } + } + } + + return list; + } + + /** + * Lookup the CLICS Big5 acronum for a judge's submission folder + * + * @param dir the folder name + * @return the acronym, or null if the folder is non-standard + */ + public String getAcronymForSubmissionDirectory(String dir) { + if(submissionDirectoryToAcronym.containsKey(dir)) { + return submissionDirectoryToAcronym.get(dir); + } + return null; + } + + /** + * Load Judge's samples judgement types + * @param cdpPath + * @throws IOException + */ + // TODO NOW use FileUtilities.getAllDirectoryEntries + private void loadCDPSampleSubmissionList(IInternalContest contest, String cdpPath) { + + List types = ListUtilities.getAllCDPSubmissionTypes(contest, cdpPath); + for(String type : types) { + super.add(new SubmissionSampleLocation("", type, getAcronymForSubmissionDirectory(type))); + } + } +} diff --git a/src/edu/csus/ecs/pc2/ui/EditFilterFrame.java b/src/edu/csus/ecs/pc2/ui/EditFilterFrame.java index cca71ebee..c42259267 100644 --- a/src/edu/csus/ecs/pc2/ui/EditFilterFrame.java +++ b/src/edu/csus/ecs/pc2/ui/EditFilterFrame.java @@ -1,10 +1,11 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.ui; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.event.KeyEvent; import java.io.PrintWriter; +import java.util.ArrayList; import javax.swing.JButton; import javax.swing.JFrame; @@ -20,7 +21,7 @@ /** * Edit a filter. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -32,7 +33,7 @@ public class EditFilterFrame extends JFrame implements UIPlugin { // TODO on close button if they say yes, invoke callback. /** - * + * */ private static final long serialVersionUID = 6498270977601785261L; @@ -60,7 +61,7 @@ public class EditFilterFrame extends JFrame implements UIPlugin { /** * This method initializes - * + * */ public EditFilterFrame() { super(); @@ -68,7 +69,7 @@ public EditFilterFrame() { } /** - * + * * @param filter * @param title * @param refreshCallback @@ -96,7 +97,7 @@ private void initialize() { /** * This method initializes mainPane - * + * * @return javax.swing.JPanel */ private JPanel getMainPane() { @@ -111,7 +112,7 @@ private JPanel getMainPane() { /** * This method initializes buttonPane - * + * * @return javax.swing.JPanel */ private JPanel getButtonPane() { @@ -130,7 +131,7 @@ private JPanel getButtonPane() { /** * This method initializes saveButton - * + * * @return javax.swing.JButton */ private JButton getApplyButton() { @@ -140,6 +141,7 @@ private JButton getApplyButton() { applyButton.setToolTipText("Apply this filter to listbox"); applyButton.setMnemonic(java.awt.event.KeyEvent.VK_A); applyButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { updateFilter(editFilterPane.getFilter()); } @@ -187,7 +189,7 @@ protected void dumpFilter(Filter filter2) { /** * This method initializes closeButton - * + * * @return javax.swing.JButton */ private JButton getCloseButton() { @@ -197,6 +199,7 @@ private JButton getCloseButton() { closeButton.setToolTipText("Close this window"); closeButton.setMnemonic(java.awt.event.KeyEvent.VK_C); closeButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { checkForChangesAndExit(); } @@ -206,15 +209,15 @@ public void actionPerformed(java.awt.event.ActionEvent e) { } protected void checkForChangesAndExit() { - - // TODO check for changes in filter. - + + // TODO check for changes in filter. + setVisible(false); } /** * This method initializes editFilterPane - * + * * @return edu.csus.ecs.pc2.ui.EditFilterPane */ private EditFilterPane getEditFilterPane() { @@ -225,16 +228,18 @@ private EditFilterPane getEditFilterPane() { return editFilterPane; } + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { this.contest = inContest; this.controller = inController; editFilterPane.setContestAndController(inContest, inController); editFilterPane.setFilter(filter); - + getReportButton().setVisible(Utilities.isDebugMode()); } + @Override public String getPluginTitle() { return "Edit Filter Frame"; } @@ -249,7 +254,7 @@ public void setFilter(Filter filter2) { /** * This method initializes okButton - * + * * @return javax.swing.JButton */ private JButton getOkButton() { @@ -258,6 +263,7 @@ private JButton getOkButton() { okButton.setText("Ok"); okButton.setMnemonic(java.awt.event.KeyEvent.VK_O); okButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { updateFilter(editFilterPane.getFilter()); setVisible(false); @@ -269,21 +275,37 @@ public void actionPerformed(java.awt.event.ActionEvent e) { /** * Show or hide list on edit filter frame. - * + * * @param listName * list to show or hide */ public void addList(ListNames listName) { editFilterPane.addList(listName); } - + public void setDisplayTeamName(DisplayTeamName displayTeamName) { editFilterPane.setDisplayTeamName(displayTeamName); } + /** + * + * @param items custom items to add + */ + public void addCustomItems(ArrayList items) { + editFilterPane.addCustomItems(items); + } + + /** + * + * @param title - title for the custom item check box list + */ + public void setCustomTitle(String title) { + editFilterPane.setCustomTitle(title); + } + /** * If filtering clarification, set this to true. - * @param filteringClarifications + * @param filteringClarifications */ public void setFilteringClarifications(boolean filteringClarifications) { editFilterPane.setFilteringClarifications(filteringClarifications); @@ -291,7 +313,7 @@ public void setFilteringClarifications(boolean filteringClarifications) { /** * This method initializes reportButton - * + * * @return javax.swing.JButton */ private JButton getReportButton() { @@ -300,6 +322,7 @@ private JButton getReportButton() { reportButton.setText("Report"); reportButton.setMnemonic(KeyEvent.VK_R); reportButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { showReport(); } @@ -307,7 +330,7 @@ public void actionPerformed(java.awt.event.ActionEvent e) { } return reportButton; } - + protected void showReport() { FilterReport report = new FilterReport(); report.setFilter(getEditFilterPane().getFilter()); diff --git a/src/edu/csus/ecs/pc2/ui/EditFilterPane.java b/src/edu/csus/ecs/pc2/ui/EditFilterPane.java index 21665541d..cd154328f 100644 --- a/src/edu/csus/ecs/pc2/ui/EditFilterPane.java +++ b/src/edu/csus/ecs/pc2/ui/EditFilterPane.java @@ -4,6 +4,7 @@ import java.awt.BorderLayout; import java.awt.GridLayout; import java.security.InvalidParameterException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.Vector; @@ -16,6 +17,7 @@ import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.SwingUtilities; +import javax.swing.border.TitledBorder; import edu.csus.ecs.pc2.core.IInternalController; import edu.csus.ecs.pc2.core.list.AccountComparator; @@ -173,8 +175,21 @@ public class EditFilterPane extends JPanePlugin { private JCheckBoxJList clientTypesListBox; // @jve:decl-index=0: + private TitledBorder customPaneBorder = null; + private JPanel customTypePane = null; + + private JScrollPane customTypeScroll = null; + + private DefaultListModel customTypeListModel = new DefaultListModel(); + + private JCheckBoxJList customTypesListBox; + private Permission permission = new Permission(); + private ArrayList customItems = new ArrayList(); + + private String customTitle = "Custom"; + /** * JList names in EditFilterPane. * @@ -233,6 +248,10 @@ public enum ListNames { * */ CLIENT_TYPES, + /** + * + */ + CUSTOM_LIST, } public EditFilterPane() { @@ -687,6 +706,15 @@ public void populateFields() { getToTimeTextField().setText("" + filter.getEndElapsedTime()); } } + + customTypeListModel.removeAllElements(); + for (Object custItem : customItems) { + WrapperJCheckBox wrapperJCheckBox = new WrapperJCheckBox(custItem); + if (filter.isFilteringCustom()) { + wrapperJCheckBox.setSelected(filter.matches(custItem)); + } + customTypeListModel.addElement(wrapperJCheckBox); + } } /** @@ -919,6 +947,16 @@ public Filter getFilter() { } } + filter.clearCustomItems(); + enumeration = customTypeListModel.elements(); + while (enumeration.hasMoreElements()) { + WrapperJCheckBox element = (WrapperJCheckBox) enumeration.nextElement(); + if (element.isSelected()) { + Object object = element.getContents(); + filter.addCustomItem(object); + } + } + filter.clearElapsedTimeRange(); if (getFromTimeTextField().getText().length() > 0){ filter.setStartElapsedTime(Long.parseLong(getFromTimeTextField().getText())); @@ -1045,11 +1083,33 @@ public void addList (ListNames listName){ case CLIENT_TYPES: listsPanel.add(getClientTypePane(), 0); break; + case CUSTOM_LIST: + listsPanel.add(getCustomTypePane(), 0); + break; default: throw new InvalidParameterException("Invalid listNames: " + listName); } } + /** + * + * @param items custom items to add + */ + public void addCustomItems(ArrayList items) { + customItems = items; + } + + /** + * + * @param title - title for the custom item check box list + */ + public void setCustomTitle(String title) { + customTitle = title; + if(customPaneBorder != null) { + customPaneBorder.setTitle(customTitle); + } + } + /** * This method initializes timeRangePane * @@ -1246,6 +1306,38 @@ private JCheckBoxJList getClientTypesListBox () { + public JPanel getCustomTypePane() { + if (customTypePane == null) { + customTypePane = new JPanel(); + customTypePane.setLayout(new BorderLayout()); + customTypePane.setName("customTypePane"); + customPaneBorder = javax.swing.BorderFactory.createTitledBorder(null, customTitle, javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", java.awt.Font.BOLD, 12), new java.awt.Color(51, 51, 51)); + customTypePane.setBorder(customPaneBorder); + customTypePane.setSize(new java.awt.Dimension(251, 151)); + customTypePane.add(getCustomTypeScroll(), java.awt.BorderLayout.CENTER); + } + + return customTypePane; + } + + private JScrollPane getCustomTypeScroll() { + if (customTypeScroll == null) { + customTypeScroll = new JScrollPane(); + customTypeScroll.setViewportView(getCustomTypesListBox()); + } + return customTypeScroll; + } + + private JCheckBoxJList getCustomTypesListBox () { + if (customTypesListBox == null) { + customTypesListBox = new JCheckBoxJList(customTypeListModel); + } + return customTypesListBox; + } + + + public JPanel getPermissionsPane() { if (permissionsPane == null) { permissionsPane = new JPanel(); diff --git a/src/edu/csus/ecs/pc2/ui/FrameUtilities.java b/src/edu/csus/ecs/pc2/ui/FrameUtilities.java index bdf7753e4..3861f6d5a 100644 --- a/src/edu/csus/ecs/pc2/ui/FrameUtilities.java +++ b/src/edu/csus/ecs/pc2/ui/FrameUtilities.java @@ -1,10 +1,17 @@ -// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.ui; +import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.Image; import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; @@ -17,13 +24,16 @@ import java.util.ArrayList; import java.util.List; +import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JDialog; import javax.swing.JFrame; +import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; import edu.csus.ecs.pc2.VersionInfo; import edu.csus.ecs.pc2.core.Constants; @@ -32,17 +42,18 @@ import edu.csus.ecs.pc2.core.log.StaticLog; import edu.csus.ecs.pc2.core.model.IInternalContest; + /** * Methods to center frame, change cursor, etc.
    * Contains method to change look and feel, set cursor state, center windows and a yes no dialog with cancel as default. - * + * * @author pc2@ecs.csus * @version $Id$ */ // $HeadURL$ public final class FrameUtilities { - + public static final String PC2_LOGO_FILENAME = "PC2Logo.png"; public static final String ICPC_BANNER_FILENAME = "ICPCWebMast_small.png"; public static final String CSUS_LOGO_FILENAME = "csus_logo.png"; @@ -50,7 +61,7 @@ public final class FrameUtilities { /** - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -59,7 +70,7 @@ public enum HorizontalPosition { }; /** - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -74,7 +85,7 @@ private FrameUtilities() { /** * Set Native Look and Feel. - * + * */ public static void setNativeLookAndFeel() { try { @@ -86,7 +97,7 @@ public static void setNativeLookAndFeel() { /** * Set Java Look and Feel. - * + * */ public static void setJavaLookAndFeel() { try { @@ -98,7 +109,7 @@ public static void setJavaLookAndFeel() { /** * Center this frame/component on the screen. - * + * * @param component */ public static void centerFrame(Component component) { @@ -108,7 +119,7 @@ public static void centerFrame(Component component) { /** * Center frame/component at top of screen. - * + * * @param component */ public static void centerFrameTop(Component component) { @@ -117,10 +128,10 @@ public static void centerFrameTop(Component component) { } /** - * + * * Center frame/component over a parentFrame. If parentFrame is null * centers across screen. If the parent is smaller, match the x and/or y. - * + * * @param parentFrame * @param component */ @@ -147,7 +158,7 @@ public static void centerFrameOver(Component parentFrame, Component component) { } /** * Center frame at top of screen. - * + * * @param component */ public static void setFrameWindowWidth(Component component) { @@ -157,7 +168,7 @@ public static void setFrameWindowWidth(Component component) { /** * Display mouse Busy or Wait cusor (usually hourglass) - * + * * @param component */ public static void waitCursor(Component component) { @@ -166,7 +177,7 @@ public static void waitCursor(Component component) { /** * Display mouse Default cursor (usually pointer) - * + * * @param component */ public static void regularCursor(Component component) { @@ -175,7 +186,7 @@ public static void regularCursor(Component component) { /** * Puts this frame/component to right of input frame. - * + * * @param sourceComponent * component relative to otherComponent * @param otherComponent @@ -188,9 +199,9 @@ public static void windowToRight(Component sourceComponent, Component otherCompo /** * Yes No Cancel dialog, default selection is Cancel. - * + * * Unlike showConfirmDialog, this dialog defaults to Cancel. - * + * * @see JOptionPane#showConfirmDialog(java.awt.Component, java.lang.Object, java.lang.String, int, int, javax.swing.Icon) * @param title * @param message @@ -229,7 +240,7 @@ public static void setFramePosition(Component component, HorizontalPosition hori int newX = component.getX(); int newY = component.getY(); - + if (verticalPosition == VerticalPosition.TOP){ newY = 20; } else if (verticalPosition == VerticalPosition.BOTTOM){ @@ -254,6 +265,7 @@ public static void showMessage(JFrame parentFrame, String strTitle, String displ final JOptionPane optionPane = new JOptionPane(displayString, JOptionPane.INFORMATION_MESSAGE); dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); optionPane.addPropertyChangeListener(new PropertyChangeListener() { + @Override public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); @@ -270,16 +282,16 @@ public void propertyChange(PropertyChangeEvent e) { centerFrameOver(parentFrame, dialog); dialog.setVisible(true); } - + /** * Set a PC^2 Frame title. - * + * * Form: * PC^2 moduleName [clockstate] ver#-build# *
    * Example: PC^2 Server (Site 1) [STOPPED] 9.1.4-1908
    * PC^2 TEAM 1 (Site 1) [STARTED] 9.1.4-1908
    - * + * * @param frame * @param moduleName * @param versionInfo @@ -302,17 +314,17 @@ public static void setFrameTitle(Frame frame, String moduleName, boolean clockSt /** * Show message to user. * @param component - * @param title + * @param title * @param message */ public static void showMessage(Component component, String title, String message) { JOptionPane.showMessageDialog(component, message, title, JOptionPane.INFORMATION_MESSAGE); } - - + + /** * Creates a frame with the input plugin. - * + * * @param plugin * @param contest * @param controller @@ -324,7 +336,7 @@ public static JFramePlugin createPluginFrame(JPanePlugin plugin, IInternalContes frame.setContestAndController(contest, controller); return frame; } - + public static void viewFile(String filename, String title, Log log) { MultipleFileViewer multipleFileViewer = new MultipleFileViewer(log); multipleFileViewer.addFilePane(title, filename); @@ -332,7 +344,7 @@ public static void viewFile(String filename, String title, Log log) { FrameUtilities.centerFrameFullScreenHeight(multipleFileViewer); multipleFileViewer.setVisible(true); } - + public static void updateRowHeights(JTable table) { try { for (int row = 0; row < table.getRowCount(); row++) { @@ -350,15 +362,15 @@ public static void updateRowHeights(JTable table) { System.out.println("Ignore "+e.getMessage()); } } - + /** * This method returns an {@link ImageIcon} for the image contained in the file whose name is specified. * It first attempts to find the file as a resource in the current jars; if not found there it falls back to looking * for the file in the file system. * If the file is found (in either place) then its checksum is verified before returning a result. - * + * * @param the name of the image file to be loaded - * + * * @return an ImageIcon for the image file, or null if the file cannot be found or if it fails checksum verification */ public static ImageIcon loadAndVerifyImageFile(String inFileName) { @@ -398,29 +410,29 @@ public static ImageIcon loadAndVerifyImageFile(String inFileName) { /** * This method verifies that the file whose filename and corresponding URL are provided are legitimate -- - * that is, that the files have the expected SHA checksum values. It first reads the file from the + * that is, that the files have the expected SHA checksum values. It first reads the file from the * specified URL, then uses the {@link MessageDigest} class to compute an SHA checksum for that file. * It then uses the given String filename to select the "correct" checksum for the file, * returning true if the checksums match, false otherwise. - * + * * @param inFileName the name of the file to be verified * @param url a URL pointing to an ImageIcon for the file - * + * * @return true if the SHA checksum for the image at the URL matches the expected checksum; false if not */ private static boolean verifyImage(String inFileName, URL url) { - + // these are the real (correct) checksums for the specified files: - + //csus_logo.png (SHA1 = 3E1762112204E9032C45D57D14BB299F9D9ECD42) byte[] csuslogoChecksum = {62, 23, 98, 17, 34, 4, -23, 3, 44, 69, -43, 125, 20, -69, 41, -97, -99, -98, -51, 66}; - + //PC2Logo.png: (SHA1 = C0D5C36C310EC7092A74A651311FC9D7B987A27D) byte[] pc2logoChecksum = {-64, -43, -61, 108, 49, 14, -57, 9, 42, 116, -90, 81, 49, 31, -55, -41, -71, -121, -94, 125}; - + //ICPCWebMast_small.png (SHA1 = D5047FE7093E1DB3281F53A83BC02B743A9DA7A4 byte[] icpcbannerChecksum = {-43, 4, 127, -25, 9, 62, 29, -77, 40, 31, 83, -88, 59, -64, 43, 116, 58, -99, -89, -92}; - + //icpc_logo.png (SHA1 = 1BFEE495B8862445370FF2CB82884FD286D63C4B) byte[] icpclogoChecksum = {27, -2, -28, -107, -72, -122, 36, 69, 55, 15, -14, -53, -126, -120, 79, -46, -122, -42, 60, 75}; @@ -429,13 +441,13 @@ private static boolean verifyImage(String inFileName, URL url) { InputStream is = url.openStream(); MessageDigest md = MessageDigest.getInstance("SHA"); md.reset(); - + // //old code: // byte[] b = new byte[1024]; // while(is.read(b) > 0) { // md.update(b); <--this produces unpredictable results depending on timing of the read; this is why the old version needed multiple "SHA checksums" // } - + //new code 27March2020 (from Tim deBoer): byte[] b = new byte[1024]; int n = is.read(b); @@ -443,13 +455,13 @@ private static boolean verifyImage(String inFileName, URL url) { md.update(b, 0, n); //<--this version updates the digest with exactly (and ONLY) the NEW bytes read... (thanks Tim) n = is.read(b); } - + byte[] digested = md.digest(); //"digested" now holds the image checksum - + //find the appropriate "correctChecksum" for the current image file byte[] correctChecksum = { -1 }; //default to a nonsensical value (must have at least one byte to avoid index-out-of-range, below) - + if (inFileName.equals("images/" + CSUS_LOGO_FILENAME)) { correctChecksum = csuslogoChecksum; } else if (inFileName.equalsIgnoreCase("images/" + PC2_LOGO_FILENAME)) { @@ -459,17 +471,17 @@ private static boolean verifyImage(String inFileName, URL url) { } else if (inFileName.equals("images/" + ICPC_LOGO_FILENAME)) { correctChecksum = icpclogoChecksum; } else { - //if we get here, the file we were given doesn't match any of the expected/known files we want to check; + //if we get here, the file we were given doesn't match any of the expected/known files we want to check; // use the (nonsensical) default (above) which should cause the checksum verification (below) to fail StaticLog.warning("FrameUtilities.verifyImage(): unrecognized image file name: '" + inFileName +"'"); } - + //if in debug mode, print out the calculated checksum values for the specified image if (edu.csus.ecs.pc2.core.Utilities.isDebugMode()) { System.out.println (); System.out.println (inFileName); System.out.print ("byte[] ChecksumX = {"); - + for (int i = 0; i < digested.length; i++) { System.out.print(digested[i]); if (i < digested.length -1) { @@ -478,7 +490,7 @@ private static boolean verifyImage(String inFileName, URL url) { } System.out.println("};"); } - + //count the number of byte in the calculated checksum which match the expected checksum int matchedBytes = 0; for (int i = 0; i < digested.length; i++) { @@ -488,28 +500,28 @@ private static boolean verifyImage(String inFileName, URL url) { break; } } - + return(matchedBytes == correctChecksum.length); - + } catch (IOException e) { StaticLog.log("verifyImage("+inFileName+")", e); } catch (NoSuchAlgorithmException e) { StaticLog.log("verifyImage("+inFileName+")", e); } - + return false; } - + public static String getExceptionMessageAndStackTrace(Exception ex, String delimiter) { StringBuffer buff = new StringBuffer(); - + if (ex != null) { buff.append("Message: " + ex.getMessage()); buff.append(Constants.NL); buff.append("Class: " + ex.getClass().getName()); buff.append(Constants.NL); - + List stackTraceLines = fetchStackTraceElements(ex, "csus"); for (String string : stackTraceLines) { buff.append(string); @@ -543,8 +555,9 @@ public static List fetchStackTraceElements(Throwable e, String pattern) } public static void showExceptionMessage(Component component, final String message, Exception ex) { - + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { JOptionPane.showMessageDialog(null, message + Constants.NL + getExceptionMessageAndStackTrace(ex, Constants.NL)); } @@ -553,9 +566,92 @@ public void run() { public static void showExceptionMessage(Component component, final String title, String message, Exception ex) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { JOptionPane.showMessageDialog(component, message, title, JOptionPane.ERROR_MESSAGE); } }); } + private static Image getScaledImage(Image srcImg, int w, int h) { + BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = resizedImg.createGraphics(); + + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(srcImg, 0, 0, w, h, null); + g2.dispose(); + + return resizedImg; + } + + /** + * create a label with question mark and on click shows a message dialog. + * + * @param messageTitle dialog title + * @param messageLines dialog message lines + * @return @return a What's this? label. + */ + public static JLabel getWhatsThisLabel(String messageTitle, String[] messageLines) { + return getToolTipLabel("", "What's This? (click for additional information)", messageTitle, + String.join("\n", messageLines)); + } + + /** + * create a label with question mark and on click shows a message dialog. + * + * @param messageTitle dialog title + * @param message dialog message + * @return a What's this JLabel + */ + // TODO REFACTOR use a getWhatsThisLabel where other What's up labels, search for getIcon("OptionPane.questionIcon") + public static JLabel getWhatsThisLabel(String messageTitle, String message) { + return getToolTipLabel("", "What's This? (click for additional information)", messageTitle, + message); + } + + /** + * create a label with question mark and on click shows a message dialog. + * + * @param buttonName + * @param toolTip + * @param messageTitle + * @param messageLines + */ + public static JLabel getToolTipLabel(String buttonName, String toolTip, String messageTitle, + String[] messageLines) { + return getToolTipLabel(buttonName, toolTip, messageTitle, String.join("\n", messageLines)); + } + + /** + * create a label with question mark and on click shows a message dialog. + * + * @param buttonName name for button + * @param toolTip tooltip for button + * @param messageTitle + * @param message + */ + public static JLabel getToolTipLabel(String buttonName, String toolTip, String messageTitle, String message) { + + JLabel button = new JLabel(buttonName); + + Icon questionIcon = UIManager.getIcon("OptionPane.questionIcon"); + if (questionIcon == null || !(questionIcon instanceof ImageIcon)) { + // the current PLAF doesn't have an OptionPane.questionIcon that's an ImageIcon + + button.setForeground(Color.blue); + } else { + Image image = ((ImageIcon) questionIcon).getImage(); + button = new JLabel(new ImageIcon(getScaledImage(image, 20, 20))); + } + + button.setToolTipText(toolTip); + button.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + + JOptionPane.showMessageDialog(null, message, messageTitle, JOptionPane.INFORMATION_MESSAGE, null); + } + }); + button.setBorder(new EmptyBorder(0, 15, 0, 0)); + return button; + } } diff --git a/src/edu/csus/ecs/pc2/ui/ISelectedListsSetter.java b/src/edu/csus/ecs/pc2/ui/ISelectedListsSetter.java new file mode 100644 index 000000000..4e38167d3 --- /dev/null +++ b/src/edu/csus/ecs/pc2/ui/ISelectedListsSetter.java @@ -0,0 +1,22 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.ui; + +import java.util.List; + +/** + * Interface for callbacks/observers. + * + * @author Douglas A. Lane + * + */ +public interface ISelectedListsSetter { + + /** + * Provide observers a list of selected values. + * + * @param selectedValuesList list of selected values + * @param selectedIndices list of selected indexes + */ + void setSelectedValuesList(List selectedValuesList, int[] selectedIndices); + +} diff --git a/src/edu/csus/ecs/pc2/ui/JListFrame.java b/src/edu/csus/ecs/pc2/ui/JListFrame.java new file mode 100644 index 000000000..f2e3555a6 --- /dev/null +++ b/src/edu/csus/ecs/pc2/ui/JListFrame.java @@ -0,0 +1,66 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.ui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.HeadlessException; +import java.util.Arrays; +import java.util.List; + +import javax.swing.JFrame; + +/** + * Generic List frame. + * + * @see JListPane + * + * + * @author Douglas A. Lane + */ +public class JListFrame extends JFrame { + /** + * + * + */ + private static final long serialVersionUID = -5073426131461967909L; + + private JListPane listPane; + + public JListFrame(String title, Object [] items, int[] selecteditems, ISelectedListsSetter selectedListsSetter) throws HeadlessException { + super(); + setMinimumSize(new Dimension(400, 400)); + setName("JListFrame"); + setTitle(title); + setPreferredSize(new Dimension(400, 400)); + + listPane = new JListPane(this, items, selecteditems, selectedListsSetter); + getContentPane().add(listPane, BorderLayout.CENTER); + FrameUtilities.centerFrame(this); + } + + /** + * Used for unit testing. + * + * @param args + */ + public static void main(String[] args) { + + + Object[] items2 = { "One", "Two", "Three", "Four" }; + + JListFrame f = new JListFrame("hi", items2, new int[0], new ISelectedListsSetter() { + + @Override + public void setSelectedValuesList(List selectedValuesList, int[] selectedIndices) { + for (Object object : selectedValuesList) { + System.out.println("values " + object.toString()); + } + System.out.println("ind " + Arrays.toString(selectedIndices)); + } + }); + f.setVisible(true); + } + + + +} diff --git a/src/edu/csus/ecs/pc2/ui/JListPane.java b/src/edu/csus/ecs/pc2/ui/JListPane.java new file mode 100644 index 000000000..62ae7a08c --- /dev/null +++ b/src/edu/csus/ecs/pc2/ui/JListPane.java @@ -0,0 +1,166 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.ui; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +/** + * A Pane that presents the user with a list of items and provides a way to identify + * which list items are selected. On Update will return the selected items via + * the callback ISelectedListsSetter. + * + * Provides a way to show the user a list of items to select, then + * use ISelectedListsSetter to return the selected values to the calling method. + * + * As input takes a list of items and corresponding (if any) indexes of selected items. + * + * When a user selects items and clicks Update will call the ISelectedListsSetter with + * the item list and (new) list of selected indexes within that list. + * + * @author Douglas A. Lane + * + */ +// TODO NOW 232 fix bug where says there is a change, when not selection change has been made. +public class JListPane extends JPanePlugin { + /** + * + */ + private static final long serialVersionUID = -1383088589409036086L; + + @SuppressWarnings("rawtypes") + JList theList = new JList(); + + private JFrame parentFrame = null; + + private int[] holdSelectedIndexes = new int[0]; + + private ISelectedListsSetter selectedListsSetter; + + public JListPane() { + setLayout(new BorderLayout(0, 0)); + + JScrollPane scrollPane = new JScrollPane(); + add(scrollPane); + + scrollPane.setViewportView(theList); + + JPanel buttonPane = new JPanel(); + FlowLayout flowLayout = (FlowLayout) buttonPane.getLayout(); + flowLayout.setHgap(45); + add(buttonPane, BorderLayout.SOUTH); + + JButton saveButton = new JButton("Save"); + saveButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + saveAndClose(); + } + }); + buttonPane.add(saveButton); + + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + closeTheWindow(); + } + }); + buttonPane.add(cancelButton); + } + + /** + * Create a pane that has a list of objects. + * + * + * @param parentFrame parent frame for JPanel, used to close parent frame + * @param items list of items to display, must be at least one + * item + * @param selectedItemsIndexes selected items indexes + * @param selectedListsSetter must be not null, invoked if user saves + * selections/choices + */ + @SuppressWarnings("unchecked") + public JListPane(JFrame parentFrame, Object [] items, int[] selectedItemsIndexes, ISelectedListsSetter selectedListsSetter) { + this(); + + if (selectedListsSetter == null) { + throw new IllegalArgumentException("selectedListsSetter is null"); + } + if (parentFrame == null) { + throw new IllegalArgumentException("parentFrame is null"); + } + + if (items == null) { + throw new IllegalArgumentException("items is null"); + } + + if (items.length == 0) { + throw new IllegalArgumentException("No items in list"); + } + + this.parentFrame = parentFrame; + theList.removeAll(); + + theList.setListData(items); + + this.selectedListsSetter = selectedListsSetter; + + if (selectedItemsIndexes != null) { + holdSelectedIndexes = selectedItemsIndexes; + theList.setSelectedIndices(selectedItemsIndexes); + } + } + + @SuppressWarnings("unchecked") + protected void closeTheWindow() { + + if (isChanged()) { + + int result = FrameUtilities.yesNoCancelDialog(parentFrame, "Selection changed, save selection?", + "Save selection?"); + if (result == JOptionPane.YES_OPTION) { + selectedListsSetter.setSelectedValuesList(theList.getSelectedValuesList(), theList.getSelectedIndices()); + } + } + + if (parentFrame != null) { + parentFrame.setVisible(false); + parentFrame.dispose(); + } + } + + private boolean isChanged() { + int numSelected = theList.getModel().getSize(); + return numSelected != holdSelectedIndexes.length; + } + + @SuppressWarnings("unchecked") + protected void saveAndClose() { + + selectedListsSetter.setSelectedValuesList(theList.getSelectedValuesList(), theList.getSelectedIndices()); + + if (parentFrame != null) { + parentFrame.setVisible(false); + parentFrame.dispose(); + } + } + + @Override + public String getPluginTitle() { + return "generic JList picker"; + } + + public int[] getSelectedItemIndices() { + return theList.getSelectedIndices(); + } + +} diff --git a/src/edu/csus/ecs/pc2/ui/SampleResultsFrame.java b/src/edu/csus/ecs/pc2/ui/SampleResultsFrame.java new file mode 100644 index 000000000..e82592c74 --- /dev/null +++ b/src/edu/csus/ecs/pc2/ui/SampleResultsFrame.java @@ -0,0 +1,86 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.ui; + +import java.awt.Dimension; + +import edu.csus.ecs.pc2.core.IInternalController; +import edu.csus.ecs.pc2.core.model.IInternalContest; +import edu.csus.ecs.pc2.core.model.Problem; +import edu.csus.ecs.pc2.core.model.ProblemDataFiles; +import edu.csus.ecs.pc2.core.model.Run; +import edu.csus.ecs.pc2.core.model.RunFiles; +import edu.csus.ecs.pc2.list.SubmissionSample; + +/** + * Judge's sample results details + * + * @author John Buck + */ + +public class SampleResultsFrame extends javax.swing.JFrame implements UIPlugin { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private SampleResultsPane multiSetOutputViewerPane = null; + + /** + * Constructor which initializes this Frame to contain a {@link #multiSetOutputViewerPane}. + * Invokers are expected to call {@link #setContestAndController(IInternalContest, IInternalController)} + * and then {@link #setData(Run, RunFiles, Problem, ProblemDataFiles)} prior to calling + * {@link #setVisible(boolean)} on the frame. + * + */ + public SampleResultsFrame() { + super(); + initialize(); + } + + /** + * This method initializes this Frame to contain a {@link #multiSetOutputViewerPane}. + * Invokers are expected to call {@link #setContestAndController(IInternalContest, IInternalController)} + * and then {@link #setData(SubmissionSample)} prior to calling + * {@link #setVisible(boolean)} on the frame. + * + */ + private void initialize() { + this.setSize(new Dimension(860, 400)); + this.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + this.setContentPane(getMultiTestSetOutputViewerPane()); + this.setTitle("Sample Results"); + this.setLocationRelativeTo(null); + } + + public SampleResultsPane getMultiTestSetOutputViewerPane() { + if (multiSetOutputViewerPane == null) { + multiSetOutputViewerPane = new SampleResultsPane(); + } + return multiSetOutputViewerPane; + } + + @Override + public void setContestAndController(IInternalContest inContest, IInternalController inController) { + + getMultiTestSetOutputViewerPane().setContestAndController(inContest, inController); + + } + + @Override + public String getPluginTitle() { + return "Judge's Sample Submission Frame"; + } + + + public void setData(SubmissionSample sub) { + getMultiTestSetOutputViewerPane().setData(sub); + setTitle ("Sample Results for Run " + sub.getRun().getNumber()); + } + + public void clearData() + { + getMultiTestSetOutputViewerPane().resetResultsTable(); + } + +} // @jve:decl-index=0:visual-constraint="10,10" diff --git a/src/edu/csus/ecs/pc2/ui/SampleResultsPane.java b/src/edu/csus/ecs/pc2/ui/SampleResultsPane.java new file mode 100644 index 000000000..67d21534d --- /dev/null +++ b/src/edu/csus/ecs/pc2/ui/SampleResultsPane.java @@ -0,0 +1,952 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.ui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.util.Arrays; +import java.util.Vector; + +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.LineBorder; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableModel; + +import edu.csus.ecs.pc2.core.IInternalController; +import edu.csus.ecs.pc2.core.Utilities; +import edu.csus.ecs.pc2.core.log.Log; +import edu.csus.ecs.pc2.core.model.IInternalContest; +import edu.csus.ecs.pc2.core.model.Language; +import edu.csus.ecs.pc2.core.model.Problem; +import edu.csus.ecs.pc2.core.model.ProblemDataFiles; +import edu.csus.ecs.pc2.core.model.Run; +import edu.csus.ecs.pc2.core.model.RunFiles; +import edu.csus.ecs.pc2.core.model.RunTestCase; +import edu.csus.ecs.pc2.list.SubmissionSample; +import edu.csus.ecs.pc2.ui.cellRenderer.LinkCellRenderer; +import edu.csus.ecs.pc2.ui.cellRenderer.RightJustifiedCellRenderer; +import edu.csus.ecs.pc2.ui.cellRenderer.TestCaseResultCellRenderer; + +/** + * Multiple data set viewer pane. + * + * @author John Buck + * @version $Id: AutoJudgeSettingsPane.java 2825 2014-08-12 23:22:50Z boudreat $ + */ + +public class SampleResultsPane extends JPanePlugin implements TableModelListener { + + private static final long serialVersionUID = 1L; + + /** + * list of columns + */ + protected enum COLUMN { + DATASET_NUM, RESULT, TIME, JUDGE_DATA, JUDGE_OUTPUT + }; + + // define the column headers for the table of results + private String[] columnNames = { "Data Set #", "Result", "Time (s)", + "Judge's Data", "Judge's Output" }; + + private JPanel centerPanel = null; + + private JTable resultsTable; + + private JLabel lblProblemTitle; + + private JLabel lblRunID; + + private Run currentRun; + + private Problem currentProblem; + + private ProblemDataFiles currentProblemDataFiles; + + private JLabel lblLanguage; + + private JScrollPane resultsScrollPane; + + private Log log ; + + private JButton btnCancel; + + private JTabbedPane multiTestSetTabbedPane; + + private JPanel resultsPane; + + private JPanel resultsPaneHeaderPanel; + + private JPanel resultsPaneButtonPanel; + + private JButton resultsPaneCloseButton; + private JLabel lblTotalTestCases; + private JLabel lblNumTestCasesActuallyRun; + private JLabel lblNumFailedTestCases; + private Component horizontalGlue_9; + + private MultipleFileViewer currentViewer = null; + + private boolean debug = false; + + /** + * Constructs an instance of a plugin pane for viewing multi-testset output values. + * + */ + public SampleResultsPane() { + super(); + initialize(); + } + + /** + * This method initializes the pane. + * + */ + private void initialize() { + this.setLayout(new BorderLayout()); + this.setSize(new Dimension(717, 363)); + this.add(getCenterPanel(), java.awt.BorderLayout.CENTER); + + // TODO Bug 918 + + } + + @Override + public void setContestAndController(IInternalContest inContest, IInternalController inController) { + super.setContestAndController(inContest, inController); + log = getController().getLog(); + } + + @Override + public String getPluginTitle() { + return "Sample Execute Times Pane"; + } + + /** + * This method initializes and returns a JPanel containing a + * JTabbedPane holding the output results and options panes. Note that the method + * does not fill in any live data; that cannot be done until the View Pane's + * "setData()" method has been invoked, which doesn't happen until after construction + * of the View Pane is completed. + * + * @return javax.swing.JPanel + */ + private JPanel getCenterPanel() { + + if (centerPanel == null) { + + centerPanel = new JPanel(); + BorderLayout cpBorderLayout = new BorderLayout(); + cpBorderLayout.setVgap(0); + centerPanel.setLayout(cpBorderLayout); + + centerPanel.add(getResultsPane(), BorderLayout.CENTER); + + + } + return centerPanel; + } + + + /** + * Defines and returns a JPanel containing a Header Panel describing the current run, a JTable displaying + * test case results for the Run, and a button panel with various control Buttons. + * + * @return the Results Pane JPanel + */ + private JPanel getResultsPane() { + + if (resultsPane == null) { + + resultsPane = new JPanel(); + resultsPane.setName("View Execution Times"); + resultsPane.setLayout(new BorderLayout(0, 0)); + + // add a header for holding labels to the results panel + resultsPane.add(getResultsPaneHeaderPanel(),BorderLayout.NORTH); + + //add a scrollpane holding the actual test set results + resultsPane.add(getResultsScrollPane(), BorderLayout.CENTER); + + //add a button panel at the bottom + resultsPane.add(getResultsPaneButtonPanel(), BorderLayout.SOUTH); + } + return resultsPane; + + } + + /** + * Defines a header panel for the results pane containing information about the run whose results are being displayed. + * Note that this accessor does not fill in actual data; that cannot be done until the Test Results pane is populated + * via a call to {@link #setData(Run, RunFiles, Problem, ProblemDataFiles)}. + * + * @return a JPanel containing run information + */ + private JPanel getResultsPaneHeaderPanel() { + + if (resultsPaneHeaderPanel == null) { + + resultsPaneHeaderPanel = new JPanel(); + resultsPaneHeaderPanel.setBorder(new LineBorder(Color.BLUE, 2)); + + resultsPaneHeaderPanel.add(getRunIDLabel()); + + Component horizontalGlue_1 = Box.createHorizontalGlue(); + horizontalGlue_1.setPreferredSize(new Dimension(20, 20)); + resultsPaneHeaderPanel.add(horizontalGlue_1); + + // add a label to the header showing the Problem for which this set of test results applies + resultsPaneHeaderPanel.add(getProblemTitleLabel()); + + Component horizontalGlue = Box.createHorizontalGlue(); + horizontalGlue.setPreferredSize(new Dimension(20, 20)); + resultsPaneHeaderPanel.add(horizontalGlue); + + Component horizontalGlue_2 = Box.createHorizontalGlue(); + horizontalGlue_2.setPreferredSize(new Dimension(20, 20)); + resultsPaneHeaderPanel.add(horizontalGlue_2); + + resultsPaneHeaderPanel.add(getLanguageLabel()); + + Component horizontalGlue_8 = Box.createHorizontalGlue(); + horizontalGlue_8.setPreferredSize(new Dimension(20, 20)); + resultsPaneHeaderPanel.add(horizontalGlue_8); + resultsPaneHeaderPanel.add(getTotalTestCasesLabel()); + resultsPaneHeaderPanel.add(getHorizontalGlue_9()); + + // add a label to the header showing the total number of test cases for this problem + resultsPaneHeaderPanel.add(getNumTestCasesActuallyRunLabel()); + + Component horizontalGlue_7 = Box.createHorizontalGlue(); + horizontalGlue_7.setPreferredSize(new Dimension(20, 20)); + resultsPaneHeaderPanel.add(horizontalGlue_7); + + resultsPaneHeaderPanel.add(getNumFailedTestCasesLabel()); + } + return resultsPaneHeaderPanel; + } + + /** + * Returns a {@link JScrollPane} containing a {@link JTable} for holding test case results. + * @return + */ + private JScrollPane getResultsScrollPane() { + + if (resultsScrollPane == null) { + + // add a scrollpane to hold the table of results + resultsScrollPane = new JScrollPane(); + + // create an (empty) table of results and put it in the scrollpane + resultsTable = new JTable(12,7); + resultsTable.setValueAt(true, 0, 0); + resultsScrollPane.setViewportView(resultsTable); + } + return resultsScrollPane; + } + + /** + * Returns a JPanel containing control buttons for the Results Pane. + * @return the resultsPaneButtonPanel + */ + private JPanel getResultsPaneButtonPanel() { + + if (resultsPaneButtonPanel == null) { + + resultsPaneButtonPanel = new JPanel(); + + // add a control button to dismiss the frame + resultsPaneButtonPanel.add(getResultsPaneCloseButton()); + } + return resultsPaneButtonPanel; + } + + /** + * Returns a JButton whose action is to depends on whether it is invoked from the Options pane or the Results pane. + * If invoked from the Options pane, it simply makes the Results pane the active pane. + * If invoked from the Results pane, it closes any open child windows (such as a {@link MultiFileComparator}), and + * then disposes this MultiTestSetOutputViewerPane's parent window. + * + * Note that the Close button appears on both the Options pane and the Results pane -- but it is the same button, + * not two different instances (this is a result of the Singleton pattern implementation). + * + * Addendum: the above was the INTENT, and it is legal in Java/Swing to code it that way. However, doing so generates + * an error in the WindowBuilder (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=341111). As a result, the code was + * refactored to provide two distinct Close buttons: one for the Options pane and a different one for the Results pane. + * + * @return the Close JButton + */ + private JButton getResultsPaneCloseButton() { + if (resultsPaneCloseButton == null) { + resultsPaneCloseButton = new JButton("Close"); + + //add an action handler for the Close button + resultsPaneCloseButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + Window parentFrame = SwingUtilities.getWindowAncestor(resultsPaneCloseButton); + if (parentFrame != null) { + parentFrame.dispose(); + } + } + }); + } + return resultsPaneCloseButton; + } + + private JLabel getTotalTestCasesLabel() { + if (lblTotalTestCases == null) { + lblTotalTestCases = new JLabel("Total Test Cases: XXX"); + } + return lblTotalTestCases; + } + + /** + * @return + */ + private JLabel getNumTestCasesActuallyRunLabel() { + if (lblNumTestCasesActuallyRun == null) { + lblNumTestCasesActuallyRun = new JLabel("Test Cases Run: XXX"); + } + return lblNumTestCasesActuallyRun; + } + + /** + * @return + */ + private JLabel getNumFailedTestCasesLabel() { + if (lblNumFailedTestCases == null) { + lblNumFailedTestCases = new JLabel("Failed: XXX"); + } + return lblNumFailedTestCases; + } + + private JLabel getLanguageLabel() { + if (lblLanguage == null) { + lblLanguage = new JLabel("Language: XXX"); + } + return lblLanguage; + } + + private JLabel getRunIDLabel() { + if (lblRunID == null) { + lblRunID = new JLabel("Run ID: XXX"); + } + + return lblRunID; + } + + private JLabel getProblemTitleLabel() { + if (lblProblemTitle == null) { + lblProblemTitle = new JLabel("Problem: XXX"); + } + return lblProblemTitle; + } + + private void populateGUI() { + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + // fill in the basic header information + String letter = currentProblem.getLetter(); + String shortName = currentProblem.getShortName(); + // HACK around EditProblemPane not requiring (or setting) a letter + if (letter == null || letter.equals("null") || letter.equals("")) { + Problem[] problems = getContest().getProblems(); + for (int i = 0; i < problems.length; i++) { + if (problems[i].equals(currentProblem)) { + int asciiLetter = i+65; + letter = Character.toString((char)asciiLetter); + } + } + } + // HACK around EditProblempane not requiring (or setting) a shortName + if (shortName == null || shortName.equals("")) { + shortName = currentProblem.getDisplayName().toLowerCase().trim(); + int spaceIndex = shortName.indexOf(" "); + if (spaceIndex > 0) { + shortName = shortName.substring(0, spaceIndex); + } + } + getProblemTitleLabel().setText("Problem: " + letter + " - " + shortName); + getRunIDLabel().setText("Run ID: " + currentRun.getNumber()); + getLanguageLabel().setText("Language: " + getCurrentRunLanguageName()); + + //display in the GUI the total test cases configured in the problem + int totalTestCases = currentProblem.getNumberTestCases(); + getTotalTestCasesLabel().setText("Total Test Cases: " + totalTestCases); + + // get the actually-run test case results for the current run + RunTestCase[] testCases = getCurrentTestCaseResults(currentRun); + + // fill in the test case summary information + if (testCases == null || testCases.length==0) { + getNumTestCasesActuallyRunLabel().setText("Test Cases Run: 0"); + } else { + getNumTestCasesActuallyRunLabel().setText("Test Cases Run: " + testCases.length); + } + + //set the status label to the default (blank) + getNumFailedTestCasesLabel().setForeground(Color.black); + getNumFailedTestCasesLabel().setText(""); + + int failedCount = getNumFailedTestCases(testCases); + + if (!currentProblem.isValidatedProblem()) { + // problem is not validated, cannot be failed or passed + getNumFailedTestCasesLabel().setForeground(Color.black); + getNumFailedTestCasesLabel().setText("(No validator)"); + } else if (failedCount > 0) { + getNumFailedTestCasesLabel().setForeground(Color.red); + getNumFailedTestCasesLabel().setText("Failed: " + failedCount); + } else if (failedCount == 0 && testCases!=null && testCases.length>0) { + getNumFailedTestCasesLabel().setForeground(Color.green); + getNumFailedTestCasesLabel().setText("ALL PASSED"); + } + + resultsTable = getResultsTable(testCases); + getResultsScrollPane().setViewportView(resultsTable); + + // On switch of data, clear tabs that are there from previous + if(currentViewer != null) { + currentViewer.dispose(); + } + } + + }); + + } + + + private int getNumFailedTestCases(RunTestCase[] testCases) { + int failed = 0 ; + if (testCases != null) { + for (int i = 0; i < testCases.length; i++) { + if (!testCases[i].isPassed()) { + failed++; + // int num = i+1; + // System.out.println("Found failed test case: " + num); + } + } + } +// System.out.println (" (including " + failed + " failed cases)"); + return failed; + } + + /** + * Returns the name of the language used in the "current run" defined by the field "currentRun". + * + * @return the language display name as defined by the toString() method of the defined language. + */ + private String getCurrentRunLanguageName() { + Language language = getContest().getLanguage(currentRun.getLanguageId()); + return language.toString(); + } + + /** + * Looks at all the TestCaseResults for a run and filters + * that list to just the most recent. + * + * @param run + * @return most recent RunTestCaseResults + */ + private RunTestCase[] getCurrentTestCaseResults(Run run) { + RunTestCase[] testCases = null; + RunTestCase[] allTestCases = run.getRunTestCases(); + // hope the lastTestCase has the highest testNumber.... + if (allTestCases != null && allTestCases.length > 0) { + testCases = new RunTestCase[allTestCases[allTestCases.length-1].getTestNumber()]; + for (int i = allTestCases.length-1; i >= 0; i--) { + RunTestCase runTestCaseResult = allTestCases[i]; + int testCaseNumIndex = runTestCaseResult.getTestNumber()-1; + if (testCases[testCaseNumIndex] == null) { + testCases[testCaseNumIndex] = runTestCaseResult; + if (testCaseNumIndex == 0) { + break; + } + } + } + } + return testCases; + } + + /** + * Loads the result table with data for all test cases. + */ + private void loadTableWithAllTestCaseResults() { + + // get the test case results for the current run + RunTestCase[] allTestCases = getCurrentTestCaseResults(currentRun); + + //build a new table with the test cases and install it in the scrollpane + resultsTable = getResultsTable(allTestCases); + resultsScrollPane.setViewportView(resultsTable); + } + + /** + * Loads the result table with data for failed test cases (only). + * The initial test for "failed" is to check the RunTestCase isPassed() flag, which is based on the value "passed" which is + * stored in the RunTestCase when it is constructed (see @link{Executable#executeAndValidateDataSet}). + * This value is TRUE (meaning isPassed() returns true) if and only if: + * the submitted program was successfully executed + * AND the problem has a validator + * AND method validateProgram() returned true (indicating the problem was correctly solved for the specified test case) + * AND the ExecutionData object for the run indicates that the submission solved the problem for the specified data case. + * (the ExecutionData object indicates the program solved the problem if: + * the program compiled successfully + * AND the system was able to successfully execute the program + * AND the program did not exceed the runtime limit + * AND the validator program ran successfully + * AND there were no exceptions during Validator execution + * AND the result string returned by the Validator was "accepted". + * The ExecutionData object returns false (the problem was NOT solved) if any of these conditions is false. + * ) + * If any of the above conditions is not true, isPassed() returns false. + * + * The above in turn means that test cases for runs where the Problem has no Validator will be considered "failed" (isPassed()==false) + * -- but this is not really what the table should contain for "failed test cases". + * Therefore an additional check for "isValidated()" needs to be made. + * + * The same is true for test cases which were never executed -- they will be considered "failed" but they really shouldn't + * be displayed in a table of "failed test cases". + * TODO: currently there is no easy way to determine whether a Test Case was actually executed, unless we assume that the + * mere presence of a TestCaseResult indicates that it was executed... + * + */ + private void loadTableWithFailedTestCases() { + + // get the test case results for the current run + RunTestCase[] allTestCases = getCurrentTestCaseResults(currentRun); + + //extract failed cases into a Vector (list) + Vector failedTestCaseList = new Vector(); + if (allTestCases != null) { + for (int i = 0; i < allTestCases.length; i++) { + //check for actual "failure" - ignoring "no validator" and "not executed" cases + // TODO: figure out how to implement the test for "wasExecuted"... + if (!allTestCases[i].isPassed() && allTestCases[i].isValidated() /* && allTestCases[i].wasExecuted() */ ) { + failedTestCaseList.add(allTestCases[i]); + } + } + } + + //convert Vector to array + RunTestCase[] failedTestCases = failedTestCaseList.toArray(new RunTestCase[failedTestCaseList.size()]); + + //build a new table with just the failed cases and install it in the scrollpane + resultsTable = getResultsTable(failedTestCases); + resultsScrollPane.setViewportView(resultsTable); + } + + /** + * Returns a JTable containing the results information for the specified set of test cases. + * The method sets not only the table data model but also the appropriate cell renderers and + * action/mouse listeners for the table. + */ + private JTable getResultsTable(RunTestCase [] testCases) { + + final JTable localResultsTable; + + //create the results table + TableModel tableModel = new SampleResultsTableModel(getContest(), testCases, columnNames) ; + + tableModel.addTableModelListener(this); + + if (debug) { + System.out.println("In getResultsTable(); table model contains:"); + System.out.println("--------------"); + for (int row = 0; row < tableModel.getRowCount(); row++) { + for (int col = 0; col < tableModel.getColumnCount(); col++) { + System.out.print("[" + tableModel.getValueAt(row, col) + "]"); + } + System.out.println(); + } + System.out.println("--------------"); + } + + localResultsTable = new JTable(tableModel); + + //set the desired options on the table + localResultsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + localResultsTable.setFillsViewportHeight(true); + localResultsTable.setRowSelectionAllowed(false); + localResultsTable.getTableHeader().setReorderingAllowed(false); + + //initialize column renderers based on column type + + // set a centering renderer on desired table columns + DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer(); + centerRenderer.setHorizontalAlignment(SwingConstants.CENTER); + localResultsTable.getColumn(columnNames[COLUMN.DATASET_NUM.ordinal()]).setCellRenderer(centerRenderer); + + // set a LinkRenderer on those cells containing links + localResultsTable.getColumn(columnNames[COLUMN.JUDGE_OUTPUT.ordinal()]).setCellRenderer(new LinkCellRenderer()); + localResultsTable.getColumn(columnNames[COLUMN.JUDGE_DATA.ordinal()]).setCellRenderer(new LinkCellRenderer()); + + // render Result column as Pass/Fail on Green/Red (if the test case was validated), or else either "" or "(Not Executed)" + localResultsTable.getColumn(columnNames[COLUMN.RESULT.ordinal()]).setCellRenderer(new TestCaseResultCellRenderer()); + + // render Time column right-justified + localResultsTable.getColumn(columnNames[COLUMN.TIME.ordinal()]).setCellRenderer(new RightJustifiedCellRenderer()); + + // add a listener to allow users to click an output or data file name and display it + MouseAdapter linkListener = new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + JTable target = (JTable) e.getSource(); + int row = target.getSelectedRow(); + int column = target.getSelectedColumn(); + + if (Utilities.isDebugMode()) { + System.out.println("Mouse clicked in cell (" + row + "," + column + ")"); + } + + if (column != COLUMN.JUDGE_OUTPUT.ordinal() && column != COLUMN.JUDGE_DATA.ordinal()) { + //user clicked on a column that doesn't contain a link; ignore it + if (Utilities.isDebugMode()) { + System.out.println ("... ignored"); + } + return; + } + + //get the text in the JLabel at the current row/column cell + String labelString = ""; + String tooltipString = ""; + try { + JLabel targetLabel = (JLabel)target.getValueAt(row, column); + // Text shown in the cell + labelString = targetLabel.getText(); + // full filename is the tooltip, we use that to display the file. + tooltipString = targetLabel.getToolTipText(); + } catch (ClassCastException e1) { + if (log != null) { + log.warning("SampleResultsPane.getResultsTable(): expected to find a JLabel in localResultsTable; exception: " + + e1.getMessage()); + } else { + System.err.println("SampleResultsPane.getResultsTable(): expected to find a JLabel in localResultsTable; exception: " + + e1.getMessage()); + } + return; + } + + //check whether the clicked cell has a visible string in it (only cells with legitimate links to something have non-empty strings) + if (!labelString.equals("")) { + if(Utilities.isDebugMode()) { + System.out.println("Clicked on " + labelString + "(" + tooltipString + ")"); + } + String title, tabLabel; + if(column == COLUMN.JUDGE_OUTPUT.ordinal()) { + title = "Judge's Answer File"; + tabLabel= "Ans"; + } else { + title = "Judge's Input File"; + tabLabel= "In"; + } + tabLabel = tabLabel + " File #" + target.getValueAt(row, COLUMN.DATASET_NUM.ordinal()); + showFile(getCurrentViewer(), tooltipString, title, tabLabel, true); + } + //else cell was empty - ignore the the click + } + + @Override + public void mouseMoved(MouseEvent e) { + JTable target = (JTable) e.getSource(); + int column = target.columnAtPoint(e.getPoint()); + + if (column != COLUMN.JUDGE_OUTPUT.ordinal() && column != COLUMN.JUDGE_DATA.ordinal()) { + target.setCursor(Cursor.getDefaultCursor()); + return; + } + target.setCursor(new Cursor(Cursor.HAND_CURSOR)); + } + }; + localResultsTable.addMouseListener(linkListener); + localResultsTable.addMouseMotionListener(linkListener); + + return localResultsTable; + } + + + /** + * Returns an array containing only test case results in the received array which represent test cases which failed. + * + * That is, removes any "passed" or "not executed" test cases (as well as any null elements) from the received array and + * returns a new array containing whatever is left. + * + * @param testCaseResults - an array of RunTestCaseResults from which non-failures are to be removed + * @return an array of RunTestCaseResults containing only the failed test cases in the received array, + * or returns null if the received array is null or zero-length. + */ + private RunTestCase[] removeNonFailuresFromTestCaseResults(RunTestCase[] testCaseResults) { + + if (testCaseResults!=null && testCaseResults.length>0) { + + if (debug) { + System.out.println("removeNonFailuresFromTestCaseResults(): received an array containing the following test case results: ["); + for (int i=0; i testCaseResultList = new Vector(Arrays.asList(testCaseResults)); + + Vector toBeRemoved = new Vector(); + + //find all passed and non-validated results (non-validated means they couldn't be "passed") + for (RunTestCase res : testCaseResultList) { + if (res==null || res.isPassed() || !res.isValidated()) { + toBeRemoved.add(res); + } + } + //remove all found results from the list + for (RunTestCase removeRes : toBeRemoved) { + testCaseResultList.remove(removeRes); + } + + RunTestCase [] retArray = new RunTestCase[0]; + //return an array of the remaining TestCaseResults (i.e. the ones that "failed") + retArray = testCaseResultList.toArray(retArray); + + if (debug) { + System.out.println("removeNonFailuresFromTestCaseResults(): returning an array containing the following test case results: ["); + for (int i=0; i testCasesInTableModel) { + //yes, there are missing cases; add them to the table + for (int testCaseNum=testCasesInTableModel+1; testCaseNum<=totalTestCaseCount; testCaseNum++) { + //add the current unexecuted test case to the table model + + if (debug) { + System.out.println ("...adding unexecuted test case " + testCaseNum + " to results table"); + } + + //build the variable portions of the row data + String viewJudgeAnswerFile = ""; + if (currentProblem.getAnswerFileName(testCaseNum)!=null && currentProblem.getAnswerFileName(testCaseNum).length()>0) { + viewJudgeAnswerFile = "View"; + } + String viewJudgeDataFile = ""; + if (currentProblem.getDataFileName(testCaseNum)!=null && currentProblem.getDataFileName(testCaseNum).length()>0) { + viewJudgeDataFile = "View"; + } +// "Not Executed", //result string +// "-- ", //execution time (of which there is none since the test case wasn't executed) +// "", //link to team output (none since it wasn't executed) +// "", //link to team compare-with-judge label (disabled since there's no team output) +// "", //link to team stderr (none since it wasn't executed) +// viewJudgeAnswerFile, //link to judge's output (answer file) if any +// viewJudgeDataFile, //link to judge's data if any +// "", //link to validator stdout (none) +// "" //link to validator stderr (none) + SampleResultsRowData rowData = new SampleResultsRowData("Not Executed", "-- ",viewJudgeAnswerFile,viewJudgeDataFile); + tableModel.addRow( + new String(Integer.toString(testCaseNum)), //test case number + rowData); + } + + } else { + if (debug) { + System.out.println("...all test cases are already in the results table."); + } + } + } + + /** + * Clears the given JTable with empty test cases + * + * @param aResultsTable - a JTable expected to already contain one row for each test case which has been executed + */ + public void resetResultsTable() { + if(resultsTable != null) { + //get the table model which defines the current table contents + SampleResultsTableModel tableModel = (SampleResultsTableModel) resultsTable.getModel(); + + //get how many test case rows are already in the table model + int testCasesInTableModel = tableModel.getRowCount(); + + for(int row = testCasesInTableModel-1; row >= 0; row--) { + tableModel.removeRow(row); + } + + // Add exactly one row as a place holder to tell the user judging is taking place + // "Loading", //result string + // "-- ", //execution time (of which there is none since the test case wasn't executed) + // "", //link to team output (none since it wasn't executed) + // "", //link to team compare-with-judge label (disabled since there's no team output) + // "", //link to team stderr (none since it wasn't executed) + // "", //link to judge's output (answer file) if any + // "", //link to judge's data if any + // "", //link to validator stdout (none) + // "" //link to validator stderr (none) + SampleResultsRowData rowData = new SampleResultsRowData("Judging", "-- ", "", ""); + // add new row + tableModel.addRow( + new String("*"), //test case number + rowData); + getNumFailedTestCasesLabel().setForeground(Color.cyan); + getNumFailedTestCasesLabel().setText("(Loading...)"); + + } + } + + /** + * Invoked when table data changes; checks to see if the change took place in the "Select" column and if so + * updates the Compare Selected button (enables or disables it as necessary). + * + * @param e the TableModelEvent describing the change in the table model + */ + @Override + public void tableChanged(TableModelEvent e) { + int column = e.getColumn(); + TableModel model = (TableModel)e.getSource(); + String columnName = model.getColumnName(column); + } + + public void showMessage(final String message) { + JOptionPane.showMessageDialog(this, message); + } + + + public void setData(SubmissionSample sub) { + Run run = sub.getRun(); + + if(run != null) { + Problem problem = getContest().getProblem(sub.getProblem()); + ProblemDataFiles problemDataFiles = getContest().getProblemDataFile(problem); + + + currentRun = run; + currentProblem = problem; + currentProblemDataFiles = problemDataFiles; + populateGUI(); + } + } + + private Component getHorizontalGlue_9() { + if (horizontalGlue_9 == null) { + horizontalGlue_9 = Box.createHorizontalGlue(); + horizontalGlue_9.setPreferredSize(new Dimension(20, 20)); + } + return horizontalGlue_9; + } + + /** + * @return the currentViewer + */ + public MultipleFileViewer getCurrentViewer() { + if (currentViewer == null) { + currentViewer = new MultipleFileViewer(getController().getLog()); + } + return currentViewer; + } + + /** + * Uses the specified fileViewer to display the specified file, setting the title and message + * on the viewer to the specified values and invoking "setVisible()" on the viewer if desired. + * @param fileViewer - the viewer to be used + * @param file - the file to be displayed in the viewer + * @param title - the title to be set on the viewer title bar + * @param tabLabel - the label to be put on the viewer pane tab + * @param visible - whether or not to invoke setVisible(true) on the viewer + */ + private void showFile(MultipleFileViewer fileViewer, String file, String title, String tabLabel, boolean visible) { + + if (fileViewer == null || file == null) { + log = getController().getLog(); + log.log(Log.WARNING, "SampleResultsPane.showFile(): fileViewer or file is null"); + JOptionPane.showMessageDialog(getParentFrame(), + "System Error: null fileViewer or file; contact Contest Administrator (check logs)", + "System Error", JOptionPane.ERROR_MESSAGE); + return ; + } + File myFile = new File(file); + if (! myFile.isFile()) { + JOptionPane.showMessageDialog(getParentFrame(), + "Error: could not find file: " + file, + "File Missing", JOptionPane.ERROR_MESSAGE); + log = getController().getLog(); + log.warning("SampleResultsPane.showFile(): could not find file "+file); + return; + } + fileViewer.setTitle(title); + fileViewer.addFilePane(tabLabel, file); + // addFilePane always adds it first + fileViewer.setSelectedIndex(0); + fileViewer.enableCompareButton(false); + fileViewer.setInformationLabelText("File: " + myFile.getName()); + if (visible) { + fileViewer.setVisible(true); + } + } + +} // @jve:decl-index=0:visual-constraint="10,10" diff --git a/src/edu/csus/ecs/pc2/ui/SampleResultsRowData.java b/src/edu/csus/ecs/pc2/ui/SampleResultsRowData.java new file mode 100644 index 000000000..46847dcfa --- /dev/null +++ b/src/edu/csus/ecs/pc2/ui/SampleResultsRowData.java @@ -0,0 +1,54 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +/** + * + */ +package edu.csus.ecs.pc2.ui; + +/** + * @author John Buck + */ +public class SampleResultsRowData { + private String resultString; + private String time; + private String judgesOutputViewLabel; + private String judgesDataViewLabel; + /** + * @param resultString + * @param time + * @param judgesOutputViewLabel + * @param judgesDataViewLabel + */ + public SampleResultsRowData(String resultString, String time, String judgesOutputViewLabel, String judgesDataViewLabel) { + super(); + this.resultString = resultString; + this.time = time; + this.judgesOutputViewLabel = judgesOutputViewLabel; + this.judgesDataViewLabel = judgesDataViewLabel; + } + + /** + * @return the resultString + */ + public String getResultString() { + return resultString; + } + /** + * @return the time + */ + public String getTime() { + return time; + } + /** + * @return the judgesDataViewLable + */ + public String getJudgesDataViewLabel() { + return judgesDataViewLabel; + } + /** + * @return the judgesOutputViewLable + */ + public String getJudgesOutputViewLabel() { + return judgesOutputViewLabel; + } + +} diff --git a/src/edu/csus/ecs/pc2/ui/SampleResultsTableModel.java b/src/edu/csus/ecs/pc2/ui/SampleResultsTableModel.java new file mode 100644 index 000000000..78e1a7405 --- /dev/null +++ b/src/edu/csus/ecs/pc2/ui/SampleResultsTableModel.java @@ -0,0 +1,217 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.ui; + +import java.util.Vector; + +import javax.swing.JLabel; +import javax.swing.table.DefaultTableModel; + +import edu.csus.ecs.pc2.core.Utilities; +import edu.csus.ecs.pc2.core.model.ElementId; +import edu.csus.ecs.pc2.core.model.IInternalContest; +import edu.csus.ecs.pc2.core.model.Judgement; +import edu.csus.ecs.pc2.core.model.Problem; +import edu.csus.ecs.pc2.core.model.Run; +import edu.csus.ecs.pc2.core.model.RunTestCase; + +/** + * This class defines a table model for tables holding judge Sample Test Case Results. + * + * @author John Buck + * + */ +public class SampleResultsTableModel extends DefaultTableModel { + private static final long serialVersionUID = 1L; + + private boolean debug = false; + + + /** + * Constructs a Table Model for Sample Test Case Results. The data and columnName values + * are stored in Vectors in the DefaultTableModel parent class. + * @param contest - the contest to which the results in this table model apply (needed for access to problem configuration) + * @param testCaseResults - the data to go in the model + * @param columnNames - Strings defining the table column headers + */ + public SampleResultsTableModel(IInternalContest contest, RunTestCase[] testCaseResults, Object[] columnNames) { + + //create a default table model with zero rows and columns + super (); + + //add the column names to the model + for (int col=0; col0) { + //there is a judge's answer file for this problem for this test case + judgesOutputViewLabel.setText(answerFileName); + judgesFileName = Utilities.locateJudgesDataFile(prob, answerFileName, extPath); + if(judgesFileName == null) { + judgesFileName = answerFileName; + } + judgesOutputViewLabel.setToolTipText(judgesFileName); + } + //update judge's data file link if the problem has a judge's data file + String dataFileName = prob.getDataFileName(row+1); + if (dataFileName!=null && dataFileName.length()>0) { + //there is a judge's data file for this problem for this test case + judgesDataViewLabel.setText(dataFileName); + judgesFileName = Utilities.locateJudgesDataFile(prob, dataFileName, extPath); + if(judgesFileName == null) { + judgesFileName = dataFileName; + } + judgesDataViewLabel.setToolTipText(judgesFileName); + } + } + } + } + } + + // build the row object and add it to the model + Object[] rowData = new Object[] { testCaseNum, resultLabel, time, + judgesDataViewLabel, judgesOutputViewLabel}; + + super.addRow(rowData); + } + } + } + + /** + * Returns the Class of objects contained in the specified table column. + * The Class is determined by the type of object in the first row of + * the specified column. + */ + @Override + public Class getColumnClass(int col) { + //the data for the model is stored in the parent class vector "dataVector" + @SuppressWarnings("unchecked") + Vector v = (Vector)dataVector.elementAt(0); + return v.elementAt(col).getClass(); + } + + @Override + public int getRowCount() { + return dataVector.size(); + } + + @Override + public int getColumnCount() { + return columnIdentifiers.size(); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + return ((Vector)(dataVector.elementAt(rowIndex))).elementAt(columnIndex); + } + + /** + * Returns whether the specified cell is editable, which in the case of a cell containing a checkbox means whether the checkbox can be changed. + * The only editable cells in a Test Case Results table are those in the "select row" + * column; all other cells in all other columns are not editable. Further, the "select row" + * column should only be editable (selectable) when the "Results" column contains either the string + * "Passed" or the string "Failed". (This keeps "Not Executed" test cases from being selectable). + */ + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + + return false; + } + + /** + * Allows adding a row to the table model. + * This method delegates to super.add(); i.e., the add() method in {@link TableModel}. + */ + public void addRow(String testCaseNum, SampleResultsRowData data ) { + + //test case result (passed/failed) + JLabel resultLabel = new JLabel(data.getResultString()); + + //link for viewing judge's output + JLabel judgesOutputViewJLabel = new JLabel(data.getJudgesOutputViewLabel()); + + JLabel judgesDataViewJLabel = new JLabel(data.getJudgesDataViewLabel()); + + + //build the row object and add it to the model + Object [] rowData = new Object [] {testCaseNum, resultLabel, data.getTime(), + judgesDataViewJLabel, judgesOutputViewJLabel }; + + super.addRow(rowData); + } + + @Override + public void addRow(Object [] rowData) { + super.addRow(rowData); + } + +} diff --git a/src/edu/csus/ecs/pc2/ui/SubmitSampleRunsPane.java b/src/edu/csus/ecs/pc2/ui/SubmitSampleRunsPane.java new file mode 100644 index 000000000..102299cf5 --- /dev/null +++ b/src/edu/csus/ecs/pc2/ui/SubmitSampleRunsPane.java @@ -0,0 +1,1391 @@ +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +package edu.csus.ecs.pc2.ui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Level; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.RowSorter; +import javax.swing.RowSorter.SortKey; +import javax.swing.SortOrder; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.filechooser.FileFilter; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumnModel; +import javax.swing.table.TableModel; +import javax.swing.table.TableRowSorter; + +import edu.csus.ecs.pc2.core.FileUtilities; +import edu.csus.ecs.pc2.core.IInternalController; +import edu.csus.ecs.pc2.core.Utilities; +import edu.csus.ecs.pc2.core.list.StringToDoubleComparator; +import edu.csus.ecs.pc2.core.list.StringToNumberComparator; +import edu.csus.ecs.pc2.core.log.Log; +import edu.csus.ecs.pc2.core.log.StaticLog; +import edu.csus.ecs.pc2.core.model.ClientId; +import edu.csus.ecs.pc2.core.model.ClientSettings; +import edu.csus.ecs.pc2.core.model.ElementId; +import edu.csus.ecs.pc2.core.model.Filter; +import edu.csus.ecs.pc2.core.model.IInternalContest; +import edu.csus.ecs.pc2.core.model.IRunListener; +import edu.csus.ecs.pc2.core.model.Judgement; +import edu.csus.ecs.pc2.core.model.JudgementRecord; +import edu.csus.ecs.pc2.core.model.Language; +import edu.csus.ecs.pc2.core.model.Problem; +import edu.csus.ecs.pc2.core.model.ProblemDataFiles; +import edu.csus.ecs.pc2.core.model.Run; +import edu.csus.ecs.pc2.core.model.Run.RunStates; +import edu.csus.ecs.pc2.core.model.RunEvent; +import edu.csus.ecs.pc2.core.model.RunTestCase; +import edu.csus.ecs.pc2.core.security.Permission; +import edu.csus.ecs.pc2.imports.ccs.IContestLoader; +import edu.csus.ecs.pc2.list.ListUtilities; +import edu.csus.ecs.pc2.list.SubmissionSample; +import edu.csus.ecs.pc2.list.SubmissionSampleLocation; +import edu.csus.ecs.pc2.list.SubmissionSolutionList; +import edu.csus.ecs.pc2.ui.EditFilterPane.ListNames; +import edu.csus.ecs.pc2.ui.cellRenderer.LinkCellRenderer; +import edu.csus.ecs.pc2.ui.team.QuickSubmitter; + +/** + * A UI that to submit files found in a CDP. + * + * + * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu + */ +public class SubmitSampleRunsPane extends JPanePlugin { + + private static final int VERT_PAD = 2; + private static final int HORZ_PAD = 20; + private static final int SUBMISSION_WAIT_TIMEOUT_MS = 10000; + + private static final int ELAPSED_TIME_COLUMN = 5; + + /** + * ClientSettings key for CDP Path + */ + private static final String CUSTOM_SUBMIT_SAMPLE_CDP_PATH = "CustomSubmitSampleCDPPath"; + + private static final long serialVersionUID = -8862440024499524533L; + + private JTextField cdpTextField; + + private JLabel messageLabel; + + private QuickSubmitter submitter = new QuickSubmitter(); + + /** + * List of selected solutions names and dirs. + */ + private SubmissionSolutionList submissionSolutionList = null; + + private JPanel centerPane = null; + private JButton filterButton = null; + + private JScrollPane scrollPane = null; + private JTableCustomized runTable = null; + private DefaultTableModel runTableModel = null; + + private JPanel messagePanel = null; + private JLabel rowCountLabel = null; + + private JCheckBox overrideStopOnError = null; + + /** + * User filter + */ + private Filter filter = new Filter(); + + private EditFilterFrame editFilterFrame = null; + + private String filterFrameTitle = "Judges' Submissions filter"; + + private SampleResultsFrame sampleResultsFrame = null; + + private Log log; + + private List submissionFileList = null; + private int currentSubmission = -1; + private List submissionList = null; + private HashSet runsAdded = new HashSet(); + + private Timer submissionWaitTimer = null; + private TimerTask submissionTimerTask = null; + + // Lightish green for match + private Color matchColorSuccess = new Color(128, 255, 128); + // Lightish red for hard non-match + private Color matchColorFailure = new Color(255, 128, 128); + // Lightish yellow for take a look/maybe's/indeterminates + private Color matchColorMaybe = new Color(255, 255, 128); + // Lightish cyan for pending + private Color matchColorPending = new Color(128, 255, 255); + + + // TODO 232 remove debug22Flag + private boolean debug22Flag = false; + + // TODO On Admin update of Languages or Update of Problems - clear selected index arrays + + public SubmitSampleRunsPane() { + super(); + setLayout(new BorderLayout()); + + add(getMessagePanel(), BorderLayout.NORTH); + add(getCenterPanel(), BorderLayout.CENTER); + add(getBottomPanel(), BorderLayout.SOUTH); + + } + + /** + * This method initializes messagePanel + * + * @return javax.swing.JPanel + */ + private JPanel getMessagePanel() { + if (messagePanel == null) { + rowCountLabel = new JLabel(); + rowCountLabel.setText("### of ###"); + rowCountLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); + rowCountLabel.setPreferredSize(new java.awt.Dimension(100,16)); + + messageLabel = new JLabel("message label"); + messageLabel.setFont(new Font("Tahoma", Font.PLAIN, 14)); + messageLabel.setForeground(Color.RED); + messageLabel.setHorizontalAlignment(SwingConstants.CENTER); +// messageLabel.setPreferredSize(new Dimension(400, 32)); + + messagePanel = new JPanel(); + messagePanel.setLayout(new BorderLayout()); + messagePanel.setPreferredSize(new java.awt.Dimension(30, 30)); + messagePanel.add(messageLabel, java.awt.BorderLayout.CENTER); + messagePanel.add(rowCountLabel, java.awt.BorderLayout.EAST); + } + return messagePanel; + } + + private JPanel getCenterPanel() { + JPanel centerPane = new JPanel(); + centerPane.setLayout(new BorderLayout()); + + JPanel controlsPane = new JPanel(); + + controlsPane.setLayout(new FlowLayout()); + + JLabel cdpConfigDirLabel = new JLabel("CDP config folder: "); + cdpConfigDirLabel.setToolTipText("CDP Location for judges' samples source files"); + cdpConfigDirLabel.setHorizontalAlignment(SwingConstants.RIGHT); + controlsPane.add(cdpConfigDirLabel); + + cdpTextField = new JTextField(); + cdpTextField.setFont(new Font("Tahoma", Font.PLAIN, 12)); + Dimension cdpDim = cdpTextField.getPreferredSize(); + cdpDim.setSize(cdpDim.getWidth(), 27); + cdpTextField.setPreferredSize(cdpDim); + controlsPane.add(cdpTextField); + cdpTextField.setColumns(40); + + cdpTextField.addKeyListener(new java.awt.event.KeyAdapter() { + @Override + public void keyPressed(java.awt.event.KeyEvent e) { + if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) { + if (new File(cdpTextField.getText()).isDirectory()) { + updateCDPDirAndFields(cdpTextField.getText()); + } + } + + } + }); + + + JButton selectCDPButton = new JButton("..."); + selectCDPButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectNewCDP(); + } + }); + selectCDPButton.setToolTipText("Select a different CDP"); + selectCDPButton.setPreferredSize(new Dimension(56, 27)); +// selectCDPButton.setBounds(541, 56, 39, 23); + controlsPane.add(selectCDPButton); + + JButton fb = getFilterButton(); + controlsPane.add(fb); + + + centerPane.add(controlsPane, BorderLayout.NORTH); + + centerPane.add(getScrollPane(), BorderLayout.SOUTH);; + + return(centerPane); + } + + /** + * Builds the bottom panel (buttons) + * + * @return JPanel for the bottom area (buttons) + */ + private JPanel getBottomPanel() { + JPanel bottomPane = new JPanel(); + FlowLayout flowLayout = (FlowLayout) bottomPane.getLayout(); + flowLayout.setHgap(125); + + JButton submitRunButton = new JButton("Submit"); + submitRunButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + submitSelectedJudgesSolutions(); + } + }); + + JButton resetButton = new JButton("Reset"); + resetButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + resetFields(true); + } + }); + + overrideStopOnError = new JCheckBox("Stop on first failure"); + overrideStopOnError.setSelected(false); + overrideStopOnError.setToolTipText("Use this checkbox to override the problem defined behaviour of judging when a testcase fails."); + bottomPane.add(overrideStopOnError); + + resetButton.setToolTipText("Reset back to default settings"); + bottomPane.add(resetButton); + submitRunButton.setToolTipText("Submit selected sample runs"); + bottomPane.add(submitRunButton); + return(bottomPane); + } + + protected void updateCDPDirAndFields(String cdpConfigDir) { + try { + File configFile = new File(cdpConfigDir); + String cdpRootDirectory = configFile.getAbsolutePath(); +// if (configFile.getAbsolutePath().endsWith(IContestLoader.CONFIG_DIRNAME)) { +// cdpRootDirectory = configFile.getParent(); +// } + + cdpTextField.setText(cdpRootDirectory); + updateClientCDPPath(cdpRootDirectory); + } catch (Exception e) { + log.log(Level.WARNING, "Problem updating CDP Dir", e); + } + } + + /** + * Repopulate submissionSolutionList. + * + * @return + */ + public SubmissionSolutionList getSubmissionSolutionList() { + String cdpPath = cdpTextField.getText(); + + submissionSolutionList = new SubmissionSolutionList(getContest(), cdpPath); + + return submissionSolutionList; + } + + + private SubmissionSampleLocation[] toArray(SubmissionSolutionList list) { + return list.toArray(new SubmissionSampleLocation[list.size()]); + } + + protected void selectNewCDP() { + + File file = selectEntry("Select CDP"); + + File cdpDir = FileUtilities.findCDPConfigDirectory(file); + + if (cdpDir != null) { + updateCDPDirAndFields(cdpDir.getAbsolutePath()); + } else { + + int result = FrameUtilities.yesNoCancelDialog(this, + file.getAbsoluteFile() + " may not be a CDP directory, continue anyways?", "Select CDP"); + if (result == JOptionPane.YES_OPTION) { + updateCDPDirAndFields(file.getAbsolutePath()); + } + } + } + + /** + * Select yaml file/entry. + * + * @param dialogTitle + * @return + */ + protected File selectEntry(String dialogTitle) { + + JFileChooser chooser = new JFileChooser(cdpTextField.getText()); +// chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + FileFilter filterYAML = new FileNameExtensionFilter("YAML document (*.yaml)", "yaml"); + chooser.addChoosableFileFilter(filterYAML); + + chooser.setAcceptAllFileFilterUsed(false); + chooser.setFileFilter(filterYAML); + + int action = chooser.showOpenDialog(this); + chooser.setDialogTitle(dialogTitle); + + switch (action) { + case JFileChooser.APPROVE_OPTION: + File file = chooser.getSelectedFile(); + return file; + case JFileChooser.CANCEL_OPTION: + case JFileChooser.ERROR_OPTION: + default: + break; + } + return null; + + } + + void updateClientCDPPath(String path) { + ClientSettings settings = getContest().getClientSettings(); + if (settings == null) { + settings = new ClientSettings(getContest().getClientId()); + } + settings.put(CUSTOM_SUBMIT_SAMPLE_CDP_PATH, path); + + getController().updateClientSettings(settings); + } + + protected String getClientCDPPath() { + + ClientSettings settings = getContest().getClientSettings(); + if (settings != null) { + String path = settings.getProperty(CUSTOM_SUBMIT_SAMPLE_CDP_PATH); + if (path != null) { + return path; + } + } + + return null; + } + + /** + * Reset fields back to default values + * + * @param usingGui + */ + protected void resetFields(boolean isUsingGui) { + String cdpPath = getContest().getContestInformation().getJudgeCDPBasePath(); + + String clientPath = getClientCDPPath(); + + if (clientPath != null && !clientPath.equals(cdpPath)) { + int result = FrameUtilities.yesNoCancelDialog(this, "Overwrite locally saved CDP path with this (default) " + cdpPath, + "Replace CDP Path?"); + if (result == JOptionPane.NO_OPTION) { + cdpPath = clientPath; + } + } + + submissionList = null; + stopSubmissionTimer(); + clearSubmissionFiles(); + clearAllRuns(); + + + cdpTextField.setText(cdpPath); + +// getController().updateClientSettings(settings); + + } + + /** + * Submit sample solutions. + */ + protected void submitSelectedJudgesSolutions() { + showMessage(""); + + if(submissionFileList != null) { + showMessage("Please wait until the previous files have finished being submitted (" + (submissionList.size() - currentSubmission) + " remain)"); + return; + } + String warningMessage = verifyCDP (cdpTextField.getText()); + if (warningMessage != null) { + // let the user know that the CDP selected may not work + FrameUtilities.showMessage(this, "Maybe the CDP is invalid?", warningMessage); + } + + List files = ListUtilities.getAllJudgeSampleSubmissionFilenamesFromCDP(getContest(), cdpTextField.getText()); + + if (files.size() == 0) { + FrameUtilities.showMessage(this, "No Submissions", "No submissions found under " + cdpTextField.getText()); + return; + } + + // The actual filtering is done somewhat differently for potential submissions. We can still use + // the Filter object (filter) since it has all the criteria we need. + if(filter.isFilterOn()) { + // We use the custom filtering list for judge submission types, eg. accepted, wrong_answer, etc + if(filter.isFilteringCustom()) { + files = ListUtilities.filterByJudgingTypes(files, filter.getCustomList()); + } + + if(filter.isFilteringProblems()) { + ElementId[] probElems = filter.getProblemIdList(); + ArrayList probList = new ArrayList(); + + for(ElementId ele : probElems) { + Problem prob = getContest().getProblem(ele); + if(prob != null) { + probList.add(prob); + } + } + files = ListUtilities.filterByProblems(files, probList); + } + + if(filter.isFilteringLanguages()) { + ElementId[] langElems = filter.getLanguageIdList(); + ArrayList langList = new ArrayList(); + + for(ElementId ele : langElems) { + Language lang = getContest().getLanguage(ele); + if(lang != null) { + langList.add(lang); + } + } + files = ListUtilities.filterByLanguages(files, getContest(), langList); + } + } + int count = 0; + + if(Utilities.isDebugMode()) { + for (File file : files) { + count++; + System.out.println("Will submit #" + count + " file = " + file.getAbsolutePath()); + } + } else { + count = files.size(); + } + if (count == 0) { + showMessage("There are no matching CDP sample source files under " + cdpTextField.getText()); + return; + } + + // check with the user to be sure they want to submit everything. + int result = FrameUtilities.yesNoCancelDialog(this, "Submit " + count + " judge sample submissions?", + "Submit CDP submissions"); + if (result != JOptionPane.YES_OPTION) { + return; + } + showMessage("Submitting " + count + " runs."); + + submissionFileList = files; + // create submissionList to add SubmissionSample's to as we submit them. + submissionList = new ArrayList(); + currentSubmission = 0; + + submitNextSubmission(); + } + + private void submitNextSubmission() { + try { + File file = submissionFileList.get(currentSubmission); + + submissionTimerTask = new TimerTask() { + @Override + public void run() { + // Whoops! This means the submission never came in + log.severe("No submission was received from the server for #" + currentSubmission + "; cancelling remaining submissions"); + showMessage("Submission #" + currentSubmission + " not received from server"); + clearSubmissionFiles(); + stopSubmissionTimer(); + } + }; + + submissionWaitTimer = new Timer("Submission Wait Timer " + currentSubmission); + submissionWaitTimer.schedule(submissionTimerTask, SUBMISSION_WAIT_TIMEOUT_MS); + + SubmissionSample sub = submitter.sendSubmission(file, !overrideStopOnError.isSelected()); + if(sub != null) { + submissionList.add(sub); + updateRunRow(sub, getContest().getClientId(), true); + } + if(Utilities.isDebugMode()) { + System.out.println("Submitted #" + currentSubmission + " for problem " + sub.toString()); + } + } catch(Exception e) { + log.log(Level.WARNING, "Error submitting submission #" + currentSubmission, e); + clearSubmissionFiles(); + stopSubmissionTimer(); + } + } + + private void clearSubmissionFiles() { + submissionFileList = null; + currentSubmission = -1; + runsAdded.clear(); + } + + private void stopSubmissionTimer() { + if(submissionWaitTimer != null) { + submissionWaitTimer.cancel(); + submissionWaitTimer = null; + submissionTimerTask = null; + } + } + + /** + * Check whether cdpDir and model match + * @param cdpDir base dir for CDP, parent dir for config/ dir + * @return null if no issues, else a warning message about a diffence between model and cdpDir + */ + private String verifyCDP(String cdpDir) { + + Problem[] problems = getContest().getProblems(); + List files = new ArrayList<>(); + String missingProbs = ""; + String warningMsg = null; + String configDir = cdpDir + File.separator + IContestLoader.CONFIG_DIRNAME; + + if (new File(configDir).isDirectory()) { + cdpDir = configDir; + } + + for (Problem problem : problems) { + + // config\sumit\submissions\accepted\ISumit.java + if(!(new File(cdpDir + File.separator + problem.getShortName()).isDirectory())) { + if(!missingProbs.isEmpty()) { + missingProbs = missingProbs + ','; + } + missingProbs = missingProbs + problem.getShortName(); + } + } + if(!missingProbs.isEmpty()) { + warningMsg = "No problem folder found for: " + missingProbs; + } + return warningMsg; + } + + /** + * Returns the list of filenames that end in extension + * + * @param files + * @param extension + * @return + */ + public static List filterSource(List files, String extension) { + + List list = new ArrayList(); + for (File file : files) { + if (file.getName().endsWith(extension)) { + list.add(file); + } + } + return list; + } + + @Override + public String getPluginTitle() { + return "Submitter Pane"; + } + + @Override + public void setContestAndController(IInternalContest inContest, IInternalController inController) { + super.setContestAndController(inContest, inController); + + resetFields(false); + + String cdpPath = getContest().getContestInformation().getJudgeCDPBasePath(); + String clientPath = getClientCDPPath(); + + if (clientPath != null) { + cdpPath = clientPath; + } + +// xlog("CDP dir is now at " + cdpPath); + cdpTextField.setText(cdpPath); + + showMessage(""); + + submitter.setContestAndController(inContest, inController); + getEditFilterFrame().setContestAndController(inContest, inController); + + + log = inController.getLog(); + + getContest().addRunListener(new RunListenerImplementation()); + + } + + public void showMessage(final String message) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { +// xlog(message); + messageLabel.setText(message); + messageLabel.setToolTipText(message); + } + }); + } + + /** + * This method initializes filterButton + * + * @return javax.swing.JButton + */ + private JButton getFilterButton() { + if (filterButton == null) { + filterButton = new JButton(); + filterButton.setText("Filter"); + filterButton.setToolTipText("Edit Filter"); + filterButton.setMnemonic(java.awt.event.KeyEvent.VK_F); + filterButton.setPreferredSize(new Dimension(80, 27)); + filterButton.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + showFilterRunsFrame(); + } + }); + } + return filterButton; + } + + protected void showFilterRunsFrame() { + getEditFilterFrame().addList(ListNames.CUSTOM_LIST); + getEditFilterFrame().addCustomItems(getSubmissionSolutionList()); + getEditFilterFrame().setCustomTitle("Solution Type"); + getEditFilterFrame().addList(ListNames.LANGUAGES); + getEditFilterFrame().addList(ListNames.PROBLEMS); + + getEditFilterFrame().setFilter(filter); + getEditFilterFrame().validate(); + getEditFilterFrame().setVisible(true); + } + + public EditFilterFrame getEditFilterFrame() { + if (editFilterFrame == null){ + Runnable callback = new Runnable(){ + @Override + public void run() { +// reloadRunList(); + } + }; + editFilterFrame = new EditFilterFrame(filter, filterFrameTitle, callback); + } + return editFilterFrame; + } + + /** + * Set title for the Filter Frame. + * + * @param title + */ + public void setFilterFrameTitle (String title){ + filterFrameTitle = title; + if (editFilterFrame != null){ + editFilterFrame.setTitle(title); + } + } + + private ProblemDataFiles getProblemDataFiles(Run run) { + Problem problem = getContest().getProblem(run.getProblemId()); + return getContest().getProblemDataFile(problem); + } + + protected void viewOutputsAndData(SubmissionSample sub) { + + if(sub != null) { + if (getSampleResultsFrame().isVisible()) { + if (getSampleResultsFrame().getState() == Frame.ICONIFIED) { + getSampleResultsFrame().setState(javax.swing.JFrame.NORMAL); + } + } + + getSampleResultsFrame().setData(sub); + + getSampleResultsFrame().setVisible(true); + } + } + + private SampleResultsFrame getSampleResultsFrame() { + if (sampleResultsFrame == null) { + sampleResultsFrame = new SampleResultsFrame(); + sampleResultsFrame.setContestAndController(getContest(), getController()); + FrameUtilities.centerFrame(sampleResultsFrame); + } + return sampleResultsFrame; + } + + /** + * This method initializes scrollPane + * + * @return javax.swing.JScrollPane + */ + private JScrollPane getScrollPane() { + if (scrollPane == null) { + scrollPane = new JScrollPane(getRunTable()); + resetRunsListBoxColumns(); + } + return scrollPane; + } + + /** + * This method initializes the runTable + * + * @return JTableCustomized + */ + private JTableCustomized getRunTable() { + if (runTable == null) { + runTable = new JTableCustomized() { + private static final long serialVersionUID = 1L; + + // override JTable's default renderer to set the background color based on the match status + // of the sample folder the submission was found in vs. the judgment. + // Essentially stolen from ShadowCompareScoreboardPane. Thank you JohnC. + @Override + public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { + Component c = super.prepareRenderer(renderer, row, column); + + //default to normal background + c.setBackground(getBackground()); + + if(runTableModel != null) { + //map the specified row index number to the corresponding model row (index numbers can change due + // to sorting/scrolling; model row numbers never change). + // Here are the columns: + // Object[] fullColumns = { "Run Id", "Time", "Problem", "Expected", "Status", "Source", "Judge", "Language", "SubmissionSample" }; + + int modelRow = convertRowIndexToModel(row); + String submissionAcronym = submissionSolutionList.getAcronymForSubmissionDirectory((String)runTableModel.getValueAt(modelRow, 3)); + + if(submissionAcronym == null) { + c.setBackground(matchColorMaybe); + } else { + String judgment = (String)runTableModel.getValueAt(modelRow, 4); + // Format is something like: "Wrong answer:WA" + int idx = judgment.lastIndexOf(":"); + if(idx != -1) { + judgment = judgment.substring(idx+1); + if(submissionAcronym.equals(judgment)) { + c.setBackground(matchColorSuccess); + } else { + c.setBackground(matchColorFailure); + } + } else { + // No judgment yet; eg "QUEUED_FOR_COMPUTER_JUDGEMENT" or other non-judged status + c.setBackground(matchColorPending); + } + } + } + + return(c); + } + }; + + runTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent me) { + // If double-click see if we can select the run + if (me.getClickCount() == 2) { + JTable target = (JTable)me.getSource(); + if(target.getSelectedRow() != -1 && isAllowed(Permission.Type.JUDGE_RUN)) { + // Maybe we want to let them edit the run someday? + // requestSelectedRun(); + } + } + } + }); + } + return runTable; + } + + protected SubmissionSample findSelectedSubmissionSample() { + + SubmissionSample sub = null; + int[] selectedIndexes = runTable.getSelectedRows(); + + if (selectedIndexes.length >= 1) { + + try { + int modelIndex = runTable.convertRowIndexToModel(selectedIndexes[0]); + TableModel tm = runTable.getModel(); + sub = (SubmissionSample) tm.getValueAt(modelIndex, tm.getColumnCount()-1); + } catch (Exception e) { + // Just ignore exception - non-fatal. + } + } + return sub; + } + + public void clearAllRuns() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if(runTableModel != null) { + // All rows are discarded - the TM will notify the Table + runTableModel.setRowCount(0); + } + } + }); + } + + private void resetRunsListBoxColumns() { + + runTable.removeAll(); + + Object[] fullColumns = { "Run Id", "Time", "Problem", "Expected", "Status", "Max Time(s)", "Source", "Judge", "Language", "SubmissionSample" }; + Object[] columns; + + columns = fullColumns; + runTableModel = new DefaultTableModel(columns, 0) { + @Override + public boolean isCellEditable(int row, int col) { + return false; + } + }; + + runTable.setModel(runTableModel); + TableColumnModel tcm = runTable.getColumnModel(); + // Remove SubmissionSample from display - this does not REMOVE the column, just makes it so it doesn't show + tcm.removeColumn(tcm.getColumn(columns.length - 1)); + + // Sorters + TableRowSorter trs = new TableRowSorter(runTableModel); + + runTable.setRowSorter(trs); + runTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + + ArrayList sortList = new ArrayList(); + + + /* + * Column headers left justified + */ + ((DefaultTableCellRenderer)runTable.getTableHeader().getDefaultRenderer()).setHorizontalAlignment(JLabel.LEFT); + runTable.setRowHeight(runTable.getRowHeight() + VERT_PAD); + + +// DefaultTableCellRenderer rightAlign = new DefaultTableCellRenderer(); +// rightAlign.setHorizontalAlignment(JLabel.RIGHT); +// runTable.getTableHeader().getColumnModel().getColumn(ELAPSED_TIME_COLUMN).setCellRenderer(rightAlign); +// runTable.getColumnModel().getColumn(5).setCellRenderer(rightAlign); + + runTable.getColumnModel().getColumn(ELAPSED_TIME_COLUMN).setCellRenderer(new LinkCellRenderer()); + + StringToNumberComparator numericStringSorter = new StringToNumberComparator(); + StringToDoubleComparator doubleStringSorter = new StringToDoubleComparator(); + + int idx = 0; + + +// Object[] fullColumns = { "Run Id", "Time", "Problem", "Expected", "Status", "Max CPU ms", "Source", "Judge", "Language" }; + + + // These are in column order - omitted ones are straight string compare + trs.setComparator(0, numericStringSorter); + trs.setComparator(1, numericStringSorter); + trs.setComparator(5, doubleStringSorter); + // These are in sort order + sortList.add(new RowSorter.SortKey(0, SortOrder.ASCENDING)); + sortList.add(new RowSorter.SortKey(1, SortOrder.ASCENDING)); + sortList.add(new RowSorter.SortKey(2, SortOrder.ASCENDING)); + sortList.add(new RowSorter.SortKey(3, SortOrder.ASCENDING)); + sortList.add(new RowSorter.SortKey(4, SortOrder.ASCENDING)); + sortList.add(new RowSorter.SortKey(5, SortOrder.ASCENDING)); + sortList.add(new RowSorter.SortKey(6, SortOrder.ASCENDING)); + sortList.add(new RowSorter.SortKey(7, SortOrder.ASCENDING)); + sortList.add(new RowSorter.SortKey(8, SortOrder.ASCENDING)); + trs.setSortKeys(sortList); + resizeColumnWidth(runTable); + + // add a listener to allow users to click an output or data file name and display it + MouseAdapter linkListener = new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + JTable target = (JTable) e.getSource(); + int row = target.getSelectedRow(); + int column = target.getSelectedColumn(); + + if (Utilities.isDebugMode()) { + System.out.println("Mouse clicked in cell (" + row + "," + column + ")"); + } + + if (column != ELAPSED_TIME_COLUMN) { + //user clicked on a column that doesn't contain a link; ignore it + if (Utilities.isDebugMode()) { + System.out.println ("... ignored"); + } + return; + } + + //get the text in the JLabel at the current row/column cell + String labelString = ""; + try { + labelString = ((JLabel)target.getValueAt(row, column)).getText(); + } catch (ClassCastException e1) { + if (log != null) { + log.warning("SubmitSampleRunsPane.getResultsTable(): expected to find a JLabel in runTable; exception: " + + e1.getMessage()); + } else { + System.err.println("SubmitSampleRunsPane.getResultsTable(): expected to find a JLabel in runTable; exception: " + + e1.getMessage()); + } + return; + } + + //check whether the clicked cell has a visible string in it (only cells with legitimate links to something have non-empty strings) + if (!labelString.equals("")) { + if(Utilities.isDebugMode()) { + System.out.println("Clicked on " + labelString); + } + viewOutputsAndData(findSelectedSubmissionSample()); + } + //else cell was empty - ignore the the click + } + + @Override + public void mouseMoved(MouseEvent e) { + JTable target = (JTable) e.getSource(); + int column = target.columnAtPoint(e.getPoint()); + + if (column != ELAPSED_TIME_COLUMN) { + target.setCursor(Cursor.getDefaultCursor()); + return; + } + target.setCursor(new Cursor(Cursor.HAND_CURSOR)); + } + }; + runTable.addMouseListener(linkListener); + runTable.addMouseMotionListener(linkListener); + } + + private void resizeColumnWidth(JTableCustomized table) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + TableColumnAdjuster tca = new TableColumnAdjuster(table, HORZ_PAD); + tca.adjustColumns(); + } + }); + } + + /** + * Looks at all the TestCaseResults for a run and filters + * that list to just the most recent. + * + * @param run + * @return most recent RunTestCaseResults + */ + private RunTestCase[] getCurrentTestCaseResults(Run run) { + RunTestCase[] testCases = null; + RunTestCase[] allTestCases = run.getRunTestCases(); + // hope the lastTestCase has the highest testNumber.... + if (allTestCases != null && allTestCases.length > 0) { + testCases = new RunTestCase[allTestCases[allTestCases.length-1].getTestNumber()]; + for (int i = allTestCases.length-1; i >= 0; i--) { + RunTestCase runTestCaseResult = allTestCases[i]; + int testCaseNumIndex = runTestCaseResult.getTestNumber()-1; + if (testCases[testCaseNumIndex] == null) { + testCases[testCaseNumIndex] = runTestCaseResult; + if (testCaseNumIndex == 0) { + break; + } + } + } + } + return testCases; + } + + protected Object[] buildRunRow(SubmissionSample sub, ClientId judgeId) { + + try { + Run run = sub.getRun(); + + int cols = runTableModel.getColumnCount(); + Object[] s = new Object[cols]; + + int idx = 0; + +// Object[] fullColumns = { "Run Id", "Time", "Problem", "Expected", "Status", "Max CPU ms", "Source", "Judge", "Language", ["SubmissionSample"] }; + if(run != null) { + boolean autoJudgedRun = isAutoJudgedRun(run); + RunTestCase [] testCases = getCurrentTestCaseResults(run); + long maxMS = -1; + if(testCases != null) { + for(RunTestCase tc : testCases) { + if(tc.getElapsedMS() > maxMS) { + maxMS = tc.getElapsedMS(); + } + } + } + s[idx++] = Integer.toString(run.getNumber()); + s[idx++] = Long.toString(run.getElapsedMins()); + s[idx++] = getProblemTitle(sub.getProblem()); + s[idx++] = sub.getSampleType(); + s[idx++] = getJudgementResultString(run); + if(maxMS == -1) { + s[idx++] = new JLabel("N/A"); + } else { + s[idx++] = new JLabel(String.format("%d.%03ds", maxMS/1000, maxMS%1000)); + } + s[idx++] = sub.getSourceFile().getName(); + s[idx++] = getJudgesTitle(run, judgeId, autoJudgedRun); + } else { + s[idx++] = "Waiting"; + s[idx++] = "N/A"; + s[idx++] = getProblemTitle(sub.getProblem()); + s[idx++] = sub.getSampleType(); + s[idx++] = "N/A"; + s[idx++] = new JLabel("N/A"); + s[idx++] = sub.getSourceFile().getName(); + s[idx++] = "N/A"; + } + s[idx++] = getLanguageTitle(sub.getLanguage()); + s[idx++] = sub; + + return s; + } catch (Exception exception) { + StaticLog.getLog().log(Log.INFO, "Exception in buildRunRow()", exception); + } + return null; + } + + private String getLanguageTitle(ElementId languageId) { + Language language = getContest().getLanguage(languageId); + if(language != null) { + return(language.getDisplayName()); + } + return "Language ?"; + } + + private String getProblemTitle(ElementId problemId) { + Problem problem = getContest().getProblem(problemId); + if (problem != null) { + return problem.toString(); + } + return "Problem ?"; + } + + private String getJudgesTitle(Run run, ClientId judgeId, boolean autoJudgedRun) { + + String result = ""; + + if (judgeId != null) { + if (judgeId.equals(getContest().getClientId())) { + result = "Me"; + } else { + result = judgeId.getName() + " S" + judgeId.getSiteNumber(); + } + if (autoJudgedRun) { + result = result + "/AJ"; + } + } else { + result = ""; + } + return result; + } + + private boolean isAutoJudgedRun(Run run) { + if (run.isJudged()) { + if (!run.isSolved()) { + JudgementRecord judgementRecord = run.getJudgementRecord(); + if (judgementRecord != null && judgementRecord.getJudgementId() != null) { + return judgementRecord.isUsedValidator(); + } + } + } + return false; + } + + /** + * Return the judgement/status for the run. + * + * @param run + * @return a string that represents the state of the run + */ + protected String getJudgementResultString(Run run) { + + String result = ""; + String acronym = ""; + + if (run.isJudged()) { + + if (run.isSolved()) { + // oh my, this is bad, but, alas, copied from RunTable - gets the "accepted" judgment + Judgement judgment = getContest().getJudgements()[0]; + acronym = ":" + judgment.getAcronym(); + + result = judgment.getDisplayName(); + if (run.getStatus().equals(RunStates.MANUAL_REVIEW)) { + result = RunStates.MANUAL_REVIEW + " (" + result + ")"; + } + + } else { + result = "No"; + + JudgementRecord judgementRecord = run.getJudgementRecord(); + + if (judgementRecord != null && judgementRecord.getJudgementId() != null) { + Judgement judgment = getContest().getJudgement(judgementRecord.getJudgementId()); + // Get acronym now, because we tack it on later + if(judgment != null) { + acronym = ":" + judgment.getAcronym(); + } + if (judgementRecord.isUsedValidator() && judgementRecord.getValidatorResultString() != null) { + result = judgementRecord.getValidatorResultString(); + } else { + if (judgment != null) { + result = judgment.toString(); + } + } + + if (run.getStatus().equals(RunStates.MANUAL_REVIEW)) { + result = RunStates.MANUAL_REVIEW + " (" + result + ")"; + } + + if (run.getStatus().equals(RunStates.BEING_RE_JUDGED)) { + result = RunStates.BEING_RE_JUDGED.toString(); + } + } + } + + } else { + result = run.getStatus().toString(); + } + + if (run.isDeleted()) { + result = "DEL " + result; + } + + result = result + acronym; + + + return result; + } + + /** + * Find row that contains the supplied key (in last column) + * @param value - unique key - really, the SubmissionSample + * @return index of row, or -1 if not found + */ + private int getRowByKey(Object value) { + Object o; + + if(runTableModel != null) { + int col = runTableModel.getColumnCount() - 1; + for (int i = runTableModel.getRowCount() - 1; i >= 0; --i) { + o = runTableModel.getValueAt(i, col); + if (o != null && o.equals(value)) { + return i; + } + } + } + return(-1); + } + + /** + * This updates the rowCountlabel & toolTipText. It should be called only while on the swing thread. + */ + private void updateRowCount() { + if (filter.isFilterOn()){ + int totalRuns = getContest().getRuns().length; + rowCountLabel.setText(runTable.getRowCount()+" of "+totalRuns); + rowCountLabel.setToolTipText(runTable.getRowCount() + " filtered runs"); + } else { + rowCountLabel.setText("" + runTable.getRowCount()); + rowCountLabel.setToolTipText(runTable.getRowCount() + " runs"); + } + } + + public void updateRunRow(final SubmissionSample sub, final ClientId whoModifiedId, final boolean autoSizeAndSort) { + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + + ClientId whoJudgedId = whoModifiedId; + Run run = sub.getRun(); + if (run != null && run.isJudged()) { + JudgementRecord judgementRecord = run.getJudgementRecord(); + if (judgementRecord != null) { + whoJudgedId = judgementRecord.getJudgerClientId(); + } + } + + Object[] objects = buildRunRow(sub, whoJudgedId); + int rowNumber = getRowByKey(sub); + if (rowNumber == -1) { + // No row with this key - add new one + runTableModel.addRow(objects); + } else { + // Update all fields + for(int j = runTableModel.getColumnCount()-1; j >= 0; j--) { + runTableModel.setValueAt(objects[j], rowNumber, j); + } + } + + if (autoSizeAndSort) { + updateRowCount(); + resizeColumnWidth(runTable); + } + +// if (selectJudgementFrame != null) { + //TODO the selectJudgementFrame should be placed above all PC2 windows, not working when dblClicking in Windows OS +// } + } + }); + } + + public void reloadRunList() { + + if (filter.isFilterOn()){ + getFilterButton().setForeground(Color.BLUE); + getFilterButton().setToolTipText("Edit filter - filter ON"); + rowCountLabel.setForeground(Color.BLUE); + } else { + getFilterButton().setForeground(Color.BLACK); + getFilterButton().setToolTipText("Edit filter"); + rowCountLabel.setForeground(Color.BLACK); + } + + for (SubmissionSample sub : submissionList) { + ClientId clientId = null; + + Run run = sub.getRun(); + if(run != null) { + RunStates runStates = run.getStatus(); + if (!(runStates.equals(RunStates.NEW) || run.isDeleted())) { + JudgementRecord judgementRecord = run.getJudgementRecord(); + if (judgementRecord != null) { + clientId = judgementRecord.getJudgerClientId(); + } + } + } + updateRunRow(sub, clientId, false); + } + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + updateRowCount(); + resizeColumnWidth(runTable); + } + }); + } + /** + * Run Listener + * @author pc2@ecs.csus.edu + * @version $Id$ + */ + + // $HeadURL$ + public class RunListenerImplementation implements IRunListener { + + @Override + public void runAdded(RunEvent event) { + SubmissionSample sub = null; + Run run = event.getRun(); + Integer runNum = new Integer(run.getNumber()); + + if(runsAdded.contains(runNum)) { + log.log(Level.WARNING, "Duplicate runAdded event for Run id " + run.getNumber() + " ignored."); + if(Utilities.isDebugMode()) { + System.out.println("Duplicate runAdded (" + run.getNumber() + ") ignored - currentSubmission #" + currentSubmission); + } + return; + } + runsAdded.add(runNum); + if(Utilities.isDebugMode()) { + System.out.println("Got runAdded for run ID " + run.getNumber() + " - added to runsAdded hashset"); + } + + ClientId me = getContest().getClientId(); + + // We are only interested in runs we submitted + if(run.getSubmitter().equals(me)) { + // This is the last run - it has to be the one that was just added by us + sub = submissionList.get(currentSubmission); + if(sub != null) { + stopSubmissionTimer(); + sub.setRun(run); + if(Utilities.isDebugMode()) { + System.out.println("Received runAdded currentSubmission #" + currentSubmission + " for problem " + sub.toString()); + } + updateRunRow(sub, event.getWhoModifiedRun(), true); + // setup for next submission; if last one clean things up. + currentSubmission++; + if(currentSubmission >= submissionFileList.size()) { + clearSubmissionFiles(); + } else { + submitNextSubmission(); + } + } + } + } + + @Override + public void refreshRuns(RunEvent event) { + reloadRunList(); + } + + @Override + public void runChanged(RunEvent event) { + SubmissionSample sub = getSubmission(event); + if(sub != null) { + updateRunRow(sub, event.getWhoModifiedRun(), true); + } + } + + @Override + public void runRemoved(RunEvent event) { + SubmissionSample sub = getSubmission(event); + if(sub != null) { + updateRunRow(sub, event.getWhoModifiedRun(), true); + } + } + + private SubmissionSample getSubmission(RunEvent event) + { + Run run = event.getRun(); + + // We are only interested in runs we submitted + if(run.getSubmitter().equals(getContest().getClientId())) { + for(SubmissionSample sub : submissionList) { + Run subRun = sub.getRun(); + // Check run numbers if this submission has a run + if(subRun != null && subRun.getNumber() == run.getNumber()) { + sub.setRun(run); + return(sub); + } + } + } + return(null); + } + } + + +} // @jve:decl-index=0:visual-constraint="10,10" diff --git a/src/edu/csus/ecs/pc2/ui/SubmitSubmissionsPane.java b/src/edu/csus/ecs/pc2/ui/SubmitSubmissionsPane.java index 7ac40c735..e5fcf9f27 100644 --- a/src/edu/csus/ecs/pc2/ui/SubmitSubmissionsPane.java +++ b/src/edu/csus/ecs/pc2/ui/SubmitSubmissionsPane.java @@ -1,6 +1,7 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.ui; +import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Font; @@ -10,6 +11,7 @@ import java.util.List; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; @@ -19,14 +21,13 @@ import edu.csus.ecs.pc2.core.IInternalController; import edu.csus.ecs.pc2.core.model.IInternalContest; +import edu.csus.ecs.pc2.list.SubmissionSample; import edu.csus.ecs.pc2.ui.team.QuickSubmitter; -import java.awt.BorderLayout; -import javax.swing.JCheckBox; /** * A UI that to submit files found in a CDP. - * - * + * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class SubmitSubmissionsPane extends JPanePlugin { @@ -36,7 +37,7 @@ public class SubmitSubmissionsPane extends JPanePlugin { private JTextField cdptextField; private JLabel messageLabel; - + private JCheckBox checkBoxSubmitYesSamples; private JCheckBox checkBoxSubmitFailingSamples; @@ -68,14 +69,14 @@ public SubmitSubmissionsPane() { messageLabel.setHorizontalAlignment(SwingConstants.CENTER); messageLabel.setBounds(10, 11, 418, 32); centerPane.add(messageLabel); - - + + checkBoxSubmitYesSamples = new JCheckBox("Submit Yes Samples"); checkBoxSubmitYesSamples.setSelected(true); checkBoxSubmitYesSamples.setToolTipText("Only submit AC sample source"); checkBoxSubmitYesSamples.setBounds(48, 101, 265, 23); centerPane.add(checkBoxSubmitYesSamples); - + checkBoxSubmitFailingSamples = new JCheckBox("Submit Failing (non-AC) Samples"); checkBoxSubmitFailingSamples.setSelected(true); checkBoxSubmitFailingSamples.setToolTipText("Submt all non-AC (Yes) submissions"); @@ -89,6 +90,7 @@ public SubmitSubmissionsPane() { JButton submitRunButton = new JButton("Submit"); submitRunButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { submitSampleSubmissions(); } @@ -108,28 +110,33 @@ protected void submitSampleSubmissions() { if (submitall) { List files = submitter.getAllCDPsubmissionFileNames(getContest(), cdptextField.getText()); - + boolean submitYesSamples = checkBoxSubmitYesSamples.isSelected(); boolean submitNoSamples = checkBoxSubmitFailingSamples.isSelected(); - + if (! submitYesSamples || ! submitNoSamples){ files = QuickSubmitter.filterRuns (files, submitYesSamples, submitNoSamples); } - - + + int count = 1; for (File file : files) { System.out.println("Found file " + count + " " + file.getAbsolutePath()); count++; - - + + } int result = FrameUtilities.yesNoCancelDialog(this, "Submit " + files.size() + " sample submissions?", "Submit CDP submissions"); if (result == JOptionPane.YES_OPTION) { - int numberSubmitted = submitter.sendSubmissions(files); + List subList = submitter.sendSubmissions(files); + int numberSubmitted = 0; + + if(subList != null) { + numberSubmitted = subList.size(); + } if (numberSubmitted == files.size()) { showMessage("Submitted all (" + files.size() + ") files/runs."); @@ -166,6 +173,7 @@ public void setContestAndController(IInternalContest inContest, IInternalControl public void showMessage(final String message) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(message); messageLabel.setToolTipText(message); diff --git a/src/edu/csus/ecs/pc2/ui/admin/AdministratorView.java b/src/edu/csus/ecs/pc2/ui/admin/AdministratorView.java index aa1a6a88a..b5865b047 100644 --- a/src/edu/csus/ecs/pc2/ui/admin/AdministratorView.java +++ b/src/edu/csus/ecs/pc2/ui/admin/AdministratorView.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.ui.admin; import java.awt.BorderLayout; @@ -71,12 +71,13 @@ import edu.csus.ecs.pc2.ui.SitesPane; import edu.csus.ecs.pc2.ui.StandingsHTMLPane; import edu.csus.ecs.pc2.ui.StandingsTablePane; +import edu.csus.ecs.pc2.ui.SubmitSampleRunsPane; import edu.csus.ecs.pc2.ui.TeamStatusPane; import edu.csus.ecs.pc2.ui.UIPlugin; /** * Administrator GUI. - * + * * @author pc2@ecs.csus.edu */ public class AdministratorView extends JFrame implements UIPlugin, ChangeListener { @@ -122,10 +123,10 @@ public class AdministratorView extends JFrame implements UIPlugin, ChangeListene private JPanel aMessagePane = null; private JLabel messageLabel = null; - + /** * This method initializes - * + * */ public AdministratorView() { super(); @@ -134,7 +135,7 @@ public AdministratorView() { /** * This method initializes this - * + * */ private void initialize() { this.setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); @@ -142,18 +143,19 @@ private void initialize() { this.setContentPane(getJPanel()); this.setTitle("PC^2 Administrator"); this.addWindowListener(new java.awt.event.WindowAdapter() { + @Override public void windowClosing(java.awt.event.WindowEvent e) { promptAndExit(); } }); getMainTabbedPanel().addChangeListener(this); - + overRideLookAndFeel(); - + FrameUtilities.centerFrame(this); } - + private void overRideLookAndFeel(){ String value = IniFile.getValue("client.plaf"); if (value != null && value.equalsIgnoreCase("java")){ @@ -163,29 +165,31 @@ private void overRideLookAndFeel(){ FrameUtilities.setNativeLookAndFeel(); } } - + /** * Listeners for admin view. - * + * * This provides a way to refresh the admin view on refresh. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ - + // $HeadURL$ protected class AdminListeners implements UIPlugin { /** - * + * */ private static final long serialVersionUID = 3733076435840880891L; + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { - + inContest.addProfileListener(new ProfileListenerImplementation()); } + @Override public String getPluginTitle() { return "AdminListeners"; } @@ -197,18 +201,20 @@ public String getPluginTitle() { // is NOT started; if both are true then get the ScheduledStartTime, format it, and display it on the Frame. + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { this.contest = inContest; this.controller = inController; final JFrame thisFrame = this; updateProfileLabel(); - + AdminListeners adminListeners = new AdminListeners(); adminListeners.setContestAndController(inContest, inController); controller.register(adminListeners); - + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { controller.startLogWindow(contest); @@ -234,14 +240,14 @@ public void run() { try { CategoriesPane categoriesPane = new CategoriesPane(); addUIPlugin(getConfigureContestTabbedPane(), "Clar Categories", categoriesPane); - + ContestPreloadPane contestPreloadPane = new ContestPreloadPane(); addUIPlugin(getConfigureContestTabbedPane(), "Contests", contestPreloadPane); } catch (Exception e) { logException(e); } } - + AutoJudgesPane autoJudgesPane = new AutoJudgesPane(); addUIPlugin(getConfigureContestTabbedPane(), "Auto Judge", autoJudgesPane); @@ -250,7 +256,7 @@ public void run() { ICPCLoadPane icpcPane = new ICPCLoadPane(); addUIPlugin(getConfigureContestTabbedPane(), "ICPC", icpcPane); - + ImportDataPane importDataPane = new ImportDataPane(); addUIPlugin(getConfigureContestTabbedPane(), "Import Config", importDataPane); @@ -259,7 +265,7 @@ public void run() { LanguagesPane languagesPane = new LanguagesPane(); addUIPlugin(getConfigureContestTabbedPane(), "Languages", languagesPane); - + BalloonSettingsPane balloonSettingsPane = new BalloonSettingsPane(); addUIPlugin(getConfigureContestTabbedPane(), "Notifications", balloonSettingsPane); @@ -302,7 +308,7 @@ public void run() { ConnectionsTablePane connectionsPane = new ConnectionsTablePane(); addUIPlugin(getRunContestTabbedPane(), "Connections", connectionsPane); } - + ClarificationsTablePane clarificationsTablePane = new ClarificationsTablePane(); addUIPlugin(getRunContestTabbedPane(), "Clarifications", clarificationsTablePane); @@ -314,24 +320,27 @@ public void run() { logException(e); } } - + // EventFeedsPane eventFeedsPane = new EventFeedsPane(); // addUIPlugin(getRunContestTabbedPane(), "Event Feeds", eventFeedsPane); ExportDataPane exportPane = new ExportDataPane(); addUIPlugin(getRunContestTabbedPane(), "Export", exportPane); - + FinalizePane finalizePane = new FinalizePane(); addUIPlugin(getRunContestTabbedPane(), "Finalize", finalizePane); - + QuickJudgePane quickJudgePane = new QuickJudgePane(); addUIPlugin(getRunContestTabbedPane(), "Judging Utilities", quickJudgePane); + SubmitSampleRunsPane sampleRunsPane = new SubmitSampleRunsPane(); + addUIPlugin(getRunContestTabbedPane(), "Submit Samples", sampleRunsPane); + if (!controller.isSuppressLoginsPaneDisplay()) { LoginsTablePane loginsTablePane = new LoginsTablePane(); addUIPlugin(getRunContestTabbedPane(), "Logins", loginsTablePane); } - + if (Utilities.isDebugMode()) { try { MessageMonitorPane messageMonitorPane = new MessageMonitorPane(); @@ -354,11 +363,11 @@ public void run() { logException(e); } } - + if (Utilities.isDebugMode()) { try { PlaybackPane playbackPane = new PlaybackPane(); - + addUIPlugin(getRunContestTabbedPane(), "Replay", playbackPane); PluginLoadPane pane = new PluginLoadPane(); pane.setParentTabbedPane(getRunContestTabbedPane()); @@ -367,11 +376,11 @@ public void run() { logException(e); } } - + ReportPane reportPane = new ReportPane(); addUIPlugin(getRunContestTabbedPane(), "Reports", reportPane); - + ResultsComparePane resultsExportComparePane = new ResultsComparePane(); addUIPlugin(getRunContestTabbedPane(), "Results Compare", resultsExportComparePane); @@ -398,7 +407,7 @@ public void run() { setSelectedTab (getRunContestTabbedPane(), "Runs"); setSelectedTab (getConfigureContestTabbedPane(), "Accounts"); - + /** * Clock and frame title. */ @@ -410,7 +419,7 @@ public void run() { contest.addContestTimeListener(new ContestTimeListenerImplementation()); controller.register(contestClockDisplay); - + FrameUtilities.setFrameTitle(thisFrame, contest.getTitle(), contest.getContestTime().isContestRunning(), new VersionInfo()); setVisible(true); } @@ -418,15 +427,15 @@ public void run() { }); } - + /** * Set the tab for the input name. - * + * * @param tabbedPane * @param name */ protected void setSelectedTab(JTabbedPane tabbedPane, String name) { - + for (int i = 0; i < tabbedPane.getComponentCount(); i ++){ String tabTitle = tabbedPane.getTitleAt(i); // System.err.println("For "+tabbedPane.getName()+" found "+tabTitle); @@ -458,13 +467,14 @@ protected void initializeSecurityAlertWindow(IInternalContest inContest) { securityAlertLogWindow.getLog().info("Security Log Started "+versionInfo.getSystemVersionInfo()); } + @Override public String getPluginTitle() { return "Admin GUI"; } /** * This method initializes jPanel - * + * * @return javax.swing.JPanel */ private JPanel getJPanel() { @@ -480,7 +490,7 @@ private JPanel getJPanel() { /** * This method initializes mainTabbedPanel - * + * * @return javax.swing.JTabbedPane */ private JTabbedPane getMainTabbedPanel() { @@ -494,7 +504,7 @@ private JTabbedPane getMainTabbedPanel() { /** * This method initializes statusPanel - * + * * @return javax.swing.JPanel */ private JPanel getStatusPanel() { @@ -507,7 +517,7 @@ private JPanel getStatusPanel() { /** * This method initializes topPanel - * + * * @return javax.swing.JPanel */ private JPanel getTopPanel() { @@ -524,7 +534,7 @@ private JPanel getTopPanel() { /** * This method initializes exitButton - * + * * @return javax.swing.JButton */ private JButton getExitButton() { @@ -534,6 +544,7 @@ private JButton getExitButton() { exitButton.setToolTipText("Click here to Shutdown PC^2"); exitButton.setMnemonic(java.awt.event.KeyEvent.VK_X); exitButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { promptAndExit(); } @@ -576,7 +587,7 @@ protected void showLog(boolean showLogWindow) { /** * This method initializes clockPane - * + * * @return javax.swing.JPanel */ private JPanel getClockPane() { @@ -595,7 +606,7 @@ private JPanel getClockPane() { /** * This method initializes exitButtonPane - * + * * @return javax.swing.JPanel */ private JPanel getExitButtonPane() { @@ -612,7 +623,7 @@ private JPanel getExitButtonPane() { /** * This method initializes padPane - * + * * @return javax.swing.JPanel */ private JPanel getPadPane() { @@ -625,7 +636,7 @@ private JPanel getPadPane() { /** * This method initializes configureContestTabbedPane - * + * * @return javax.swing.JTabbedPane */ private JTabbedPane getConfigureContestTabbedPane() { @@ -639,7 +650,7 @@ private JTabbedPane getConfigureContestTabbedPane() { /** * This method initializes runContestTabbedPane - * + * * @return javax.swing.JTabbedPane */ private JTabbedPane getRunContestTabbedPane() { @@ -649,6 +660,7 @@ private JTabbedPane getRunContestTabbedPane() { return runContestTabbedPane; } + @Override public void stateChanged (ChangeEvent e) { if (e.getSource()==getMainTabbedPanel()) { //change all mainpanel tab text to black @@ -656,7 +668,7 @@ public void stateChanged (ChangeEvent e) { for (int i=0; i * If the Object is a Boolean, the cell is rendered Pass/Fail (green/red) based on the value of the Boolean (true==pass, false==fail). *

    - * If the received Object is a String containing either "pass", "fail", "no validator", or "not executed" then the cell is + * If the received Object is a String containing either "pass", "fail", "no validator", or "not executed" then the cell is * rendered based on the text of the string as described above. The check for the value of the String is not case-sensitive. *

    * If the received Object is a JLabel, the String text in the JLabel is fetched and then the cell is rendered in the same way - * as for Strings, above. + * as for Strings, above. * - * If the value passed to the {@link #setValue(Object)} method is anything not covered of the above-listed steps then + * If the value passed to the {@link #setValue(Object)} method is anything not covered of the above-listed steps then * the cell is rendered with a yellow background containing question marks. - * + * * @author pc2@ecs.csus.edu * */ @@ -40,8 +41,9 @@ public class TestCaseResultCellRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = 2L; + @Override public void setValue(Object value) { - + // default values setBackground(Color.yellow); setText("???"); @@ -53,13 +55,13 @@ public void setValue(Object value) { if (passed) { setPass(); } else { - setFail(); + setFail(null); } - + } else if (value instanceof String) { String text = (String)value; updateCellText(text); - + } else if (value instanceof JLabel) { String text = ((JLabel)value).getText(); updateCellText(text); @@ -69,12 +71,14 @@ public void setValue(Object value) { setBorder(new EmptyBorder(0, 0, 0, 0)); } - + private void updateCellText (String text) { if (text.equalsIgnoreCase("pass")) { setPass(); } else if (text.equalsIgnoreCase("fail")) { - setFail(); + setFail(null); + } else if (text.substring(0, 4).equalsIgnoreCase("fail")) { + setFail(text); } else if (text.equalsIgnoreCase("no validator")) { setNoValidator(); } else if (text.equalsIgnoreCase("not executed")) { @@ -83,40 +87,43 @@ private void updateCellText (String text) { setJudgingResult(); } } - + private void setPass() { setBackground(Color.green); setForeground(Color.black); setFont(new Font(getFont().getName(),Font.PLAIN, 12)); setText("Pass"); } - - private void setFail() { + + private void setFail(String text) { setBackground(Color.red); setForeground(Color.white); setFont(new Font(getFont().getName(),Font.ITALIC+Font.BOLD, 12)); - setText("Fail"); + if(text == null) { + text = "Fail"; + } + setText(text); } private void setNoValidator() { setBackground(Color.yellow); setForeground(Color.black); setFont(new Font(getFont().getName(),Font.PLAIN, 12)); - setText(""); + setText(""); } - + private void setNotExecuted() { setBackground(Color.yellow); setForeground(Color.black); setFont(new Font(getFont().getName(),Font.ITALIC, 12)); - setText("(Not Executed)"); + setText("(Not Executed)"); } - + private void setJudgingResult() { setBackground(Color.cyan); setForeground(Color.black); setFont(new Font(getFont().getName(),Font.ITALIC, 12)); - setText("(Judging)"); + setText("(Judging)"); } } diff --git a/src/edu/csus/ecs/pc2/ui/server/ServerView.java b/src/edu/csus/ecs/pc2/ui/server/ServerView.java index 1aba3328e..a9299fe90 100644 --- a/src/edu/csus/ecs/pc2/ui/server/ServerView.java +++ b/src/edu/csus/ecs/pc2/ui/server/ServerView.java @@ -1,11 +1,12 @@ -// Copyright (C) 1989-2019 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.ui.server; import java.awt.BorderLayout; +import java.awt.Dialog.ModalityType; +import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Frame; -import java.awt.Dialog.ModalityType; import java.awt.event.KeyEvent; import java.util.Date; @@ -59,11 +60,10 @@ import edu.csus.ecs.pc2.ui.ReportPane; import edu.csus.ecs.pc2.ui.SitesPane; import edu.csus.ecs.pc2.ui.UIPlugin; -import java.awt.Dimension; /** * GUI for Server. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -78,7 +78,7 @@ public class ServerView extends JFrame implements UIPlugin { private IInternalController controller = null; // @jve:decl-index=0: /** - * + * */ private static final long serialVersionUID = 4547574494017009634L; @@ -97,12 +97,12 @@ public class ServerView extends JFrame implements UIPlugin { private JButton exitButton = null; private LogWindow securityAlertLogWindow = null; - + private VersionInfo versionInfo = new VersionInfo(); /** * This method initializes - * + * */ public ServerView() { super(); @@ -115,7 +115,7 @@ private void logDebugMessage(String message) { /** * This method initializes this - * + * */ private void initialize() { this.setSize(new Dimension(800, 450)); @@ -123,6 +123,7 @@ private void initialize() { this.setTitle("Server View"); this.setContentPane(getMainViewPane()); this.addWindowListener(new java.awt.event.WindowAdapter() { + @Override public void windowClosing(java.awt.event.WindowEvent e) { promptAndExit(); } @@ -131,14 +132,14 @@ public void windowClosing(java.awt.event.WindowEvent e) { overRideLookAndFeel(); FrameUtilities.centerFrameTop(this); setVisible(true); - + showMessage("Version "+versionInfo.getVersionNumber()+" (Build "+versionInfo.getBuildNumber()+")"); - + } - + private void overRideLookAndFeel(){ - + String value = IniFile.getValue("server.plaf"); if (value != null && value.equalsIgnoreCase("java")){ FrameUtilities.setJavaLookAndFeel(); @@ -157,7 +158,7 @@ protected void promptAndExit() { try { ContestSummaryReports contestReports = new ContestSummaryReports(); contestReports.setContestAndController(model, controller); - + if (contestReports.isLateInContest()){ contestReports.generateReports(); controller.getLog().info("Reports Generated to "+contestReports.getReportDirectory()); @@ -165,7 +166,7 @@ protected void promptAndExit() { } catch (Exception e) { log.log(Log.WARNING,"Unable to create reports ", e); } - + log.info("Server "+model.getSiteNumber()+" halted"); System.exit(0); } @@ -173,25 +174,29 @@ protected void promptAndExit() { /** * Implementation for the Run Listener. - * + * * @author pc2@ecs.csus.edu - * + * */ private class RunListenerImplementation implements IRunListener { + @Override public void runAdded(RunEvent event) { logDebugMessage("Run Event " + event.getRun() + " ADDED "); } + @Override public void refreshRuns(RunEvent event) { logDebugMessage("Run Event " + event.getRun() + " REFRESH/RESET RUNS "); - + } - + + @Override public void runChanged(RunEvent event) { logDebugMessage("Run Event " + event.getRun() + " CHANGED "); } - + + @Override public void runRemoved(RunEvent event) { logDebugMessage("Run Event " + event.getRun() + " REMOVED "); } @@ -206,24 +211,28 @@ private String accountText(Account account) { } /** - * + * * @author pc2@ecs.csus.edu - * + * */ public class LoginListenerImplementation implements ILoginListener { + @Override public void loginAdded(LoginEvent event) { logDebugMessage("Login Event " + event.getAction() + " " + event.getClientId()); } + @Override public void loginRemoved(LoginEvent event) { logDebugMessage("Login Event " + event.getAction() + " " + event.getClientId()); } + @Override public void loginDenied(LoginEvent event) { logDebugMessage("Login Event " + event.getAction() + " " + event.getClientId()); } - + + @Override public void loginRefreshAll(LoginEvent event) { logDebugMessage("Login Event " + event.getAction() + " " + event.getClientId()); } @@ -231,30 +240,35 @@ public void loginRefreshAll(LoginEvent event) { /** * Implementation for a Account Event Listener. - * + * * @author pc2@ecs.csus.edu - * + * */ public class AccountListenerImplementation implements IAccountListener { + @Override public void accountAdded(AccountEvent accountEvent) { logDebugMessage("Account Event " + accountEvent.getAction() + " " + accountText(accountEvent.getAccount())); } + @Override public void accountModified(AccountEvent accountEvent) { logDebugMessage("Account Event " + accountEvent.getAction() + " " + accountText(accountEvent.getAccount())); } + @Override public void accountsAdded(AccountEvent accountEvent) { logDebugMessage("Account Event " + accountEvent.getAction() + " " + accountEvent.getAccounts().length + " accounts"); } + @Override public void accountsModified(AccountEvent accountEvent) { logDebugMessage("Account Event " + accountEvent.getAction() + " " + accountEvent.getAccounts().length + " accounts"); } + @Override public void accountsRefreshAll(AccountEvent accountEvent) { logDebugMessage("Account Event " + accountEvent.getAction() + " " + accountEvent.getAccounts().length + " accounts"); } @@ -262,36 +276,43 @@ public void accountsRefreshAll(AccountEvent accountEvent) { /** * Site Listener for use by ServerView. - * + * * @author pc2@ecs.csus.edu - * + * */ public class SiteListenerImplementation implements ISiteListener { + @Override public void siteProfileStatusChanged(SiteEvent event) { - // TODO this UI does not use a change in profile status + // TODO this UI does not use a change in profile status } + @Override public void siteAdded(SiteEvent event) { logDebugMessage("Site Event Event " + event.getAction() + " " + event.getSite()); } + @Override public void siteRemoved(SiteEvent event) { logDebugMessage("Site Event " + event.getAction() + " " + event.getSite()); } + @Override public void siteLoggedOn(SiteEvent event) { logDebugMessage("Site Event " + event.getAction() + " " + event.getSite()); } + @Override public void siteLoggedOff(SiteEvent event) { logDebugMessage("Site Event " + event.getAction() + " " + event.getSite()); } + @Override public void siteChanged(SiteEvent event) { logDebugMessage("Site Event " + event.getAction() + " " + event.getSite()); } + @Override public void sitesRefreshAll(SiteEvent event) { logDebugMessage("Site Event " + event.getAction()); } @@ -299,21 +320,25 @@ public void sitesRefreshAll(SiteEvent event) { /** * InternalContest Time for use by ServerView. - * + * * @author pc2@ecs.csus.edu * */ public class ContestTimeListenerImplementation implements IContestTimeListener { + @Override public void contestTimeAdded(ContestTimeEvent event) { } + @Override public void contestTimeRemoved(ContestTimeEvent event) { } + @Override public void contestTimeChanged(ContestTimeEvent event) { } + @Override public void contestStarted(ContestTimeEvent event) { // only update our title if the event concerns us if (model.getSiteNumber() == event.getSiteNumber()) { @@ -321,6 +346,7 @@ public void contestStarted(ContestTimeEvent event) { } } + @Override public void contestStopped(ContestTimeEvent event) { // only update our title if the event concerns us if (model.getSiteNumber() == event.getSiteNumber()) { @@ -328,12 +354,13 @@ public void contestStopped(ContestTimeEvent event) { } } + @Override public void refreshAll(ContestTimeEvent event) { if (model.getSiteNumber() == event.getSiteNumber()) { updateFrameTitle(event.getContestTime().isContestRunning()); } } - + /** This method exists to support differentiation between manual and automatic starts. * Currently it delegates the handling to the contestStarted() method, while * also popping up a notification dialog if the Server is in GUI mode and also @@ -342,10 +369,10 @@ public void refreshAll(ContestTimeEvent event) { @Override public void contestAutoStarted(ContestTimeEvent event) { contestStarted(event); - + //log the automatic-start info("Contest automatically started due to arrival of enabled scheduled start time."); - + //if using a GUI, display a popup notification of the automatic-start if (controller != null && controller.isUsingGUI()) { //Note: previously the following line of code was used for the popup; however, showMessageDialog() does @@ -369,10 +396,10 @@ public void contestAutoStarted(ContestTimeEvent event) { } } } - + /** * This method initializes mainViewPane - * + * * @return javax.swing.JPanel */ private JPanel getMainViewPane() { @@ -387,7 +414,7 @@ private JPanel getMainViewPane() { /** * Puts this frame to right of input frame. - * + * * @param sourceFrame the JFrame to which this frame is attached (that is, * the frame in which this frame will be moved to the right) */ @@ -398,7 +425,7 @@ public void windowToRight(JFrame sourceFrame) { /** * This method initializes mainTabbedPane - * + * * @return javax.swing.JTabbedPane */ private JTabbedPane getMainTabbedPane() { @@ -410,14 +437,15 @@ private JTabbedPane getMainTabbedPane() { void updateFrameTitle(final boolean turnButtonsOn) { final Frame thisFrame = this; - + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { FrameUtilities.setFrameTitle(thisFrame, "Server (Site "+model.getSiteNumber()+")", turnButtonsOn, new VersionInfo()); } }); } - + protected void registerPlugin (UIPlugin plugin){ try { controller.register (plugin); @@ -429,55 +457,58 @@ protected void registerPlugin (UIPlugin plugin){ JOptionPane.showMessageDialog(this, "Error loading " + plugin.getPluginTitle()); } } - + /** * Listeners for server view. - * + * * This provides a way to refresh the server view on refresh. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ - + // $HeadURL$ protected class ServerListeners implements UIPlugin { /** - * + * */ private static final long serialVersionUID = 3733076435840880891L; + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { - + inContest.addRunListener(new RunListenerImplementation()); inContest.addAccountListener(new AccountListenerImplementation()); inContest.addLoginListener(new LoginListenerImplementation()); inContest.addSiteListener(new SiteListenerImplementation()); inContest.addContestTimeListener(new ContestTimeListenerImplementation()); inContest.addProfileListener(new ProfileListenerImplementation()); - + setModel(inContest); setController(inController); - + populateUI(); } + @Override public String getPluginTitle() { return "ServerListeners"; } } - + + @Override public void setContestAndController(IInternalContest inContest, IInternalController inController) { - + setModel(inContest); setController(inController); - + this.log = controller.getLog(); - + populateUI(); inController.startLogWindow(model); - + initializeSecurityAlertWindow(inContest); ServerListeners serverListeners = new ServerListeners(); @@ -494,10 +525,10 @@ public void setContestAndController(IInternalContest inContest, IInternalControl logException(e); } } - + ExportDataPane exportPane = new ExportDataPane(); addUIPlugin(getMainTabbedPane(), "Export", exportPane); - + if (Utilities.isDebugMode()) { try { LoadContestPane loadContestPane = new LoadContestPane(); @@ -506,7 +537,7 @@ public void setContestAndController(IInternalContest inContest, IInternalControl logException(e); } } - + LoginsTablePane loginsPane = new LoginsTablePane(); addUIPlugin(getMainTabbedPane(), "Logins", loginsPane); @@ -527,20 +558,20 @@ public void setContestAndController(IInternalContest inContest, IInternalControl try { PacketMonitorPane packetMonitorPane = new PacketMonitorPane(); addUIPlugin(getMainTabbedPane(), "Packets", packetMonitorPane); - + PluginLoadPane pane = new PluginLoadPane(); pane.setParentTabbedPane(getMainTabbedPane()); addUIPlugin(getMainTabbedPane(), "Plugin Load", pane); - + ProfilesPane profilePane = new ProfilesPane(); addUIPlugin(getMainTabbedPane(), "Profiles", profilePane); - + PlaybackPane playbackPane = new PlaybackPane(); addUIPlugin(getMainTabbedPane(), "Replay", playbackPane); } catch (Exception e) { logException(e); } - } + } ReportPane reportPane = new ReportPane(); addUIPlugin(getMainTabbedPane(), "Reports", reportPane); @@ -550,27 +581,27 @@ public void setContestAndController(IInternalContest inContest, IInternalControl ContestTimesPane contestTimesPane = new ContestTimesPane(); addUIPlugin(getMainTabbedPane(), "Times", contestTimesPane); - + AboutPane aboutPane = new AboutPane(); addUIPlugin(getMainTabbedPane(), "About", aboutPane); - + setSelectedTab(getMainTabbedPane(), "Logins"); - + Profile profile = inContest.getProfile(); info("Using Profile: " + profile.getName() + " @ " + profile.getProfilePath()); } - + private void info(String string) { System.out.println(new Date() + " " + string); if (log != null) { - log.info(string); + log.info(string); } } /** * Update frame title and profile label. - * + * */ private void populateUI() { updateFrameTitle(model.getContestTime().isContestRunning()); @@ -580,14 +611,14 @@ private void populateUI() { public void setModel(IInternalContest inContest) { this.model = inContest; } - + public void setController(IInternalController controller) { this.controller = controller; } /** * Set the tab for the input name. - * + * * @param tabbedPane * @param name */ @@ -626,6 +657,7 @@ protected void initializeSecurityAlertWindow(IInternalContest inContest) { } + @Override public String getPluginTitle() { return "Server Main GUI"; } @@ -646,7 +678,7 @@ protected void addUIPlugin(JTabbedPane tabbedPane, String tabTitle, JPanePlugin } } - + protected void addUIPlugin(String tabTitle, JPanePlugin plugin) { try { plugin.setParentFrame(this); @@ -655,12 +687,12 @@ protected void addUIPlugin(String tabTitle, JPanePlugin plugin) { } catch (Exception e) { controller.getLog().log(Log.WARNING, "Exception loading plugin ", e); JOptionPane.showMessageDialog(this, "Error loading " + plugin.getPluginTitle()); - } + } } /** * This method initializes messagePanel - * + * * @return javax.swing.JPanel */ private JPanel getMessagePanel() { @@ -672,6 +704,7 @@ private JPanel getMessagePanel() { messageLabel.setFont(new Font("Dialog", Font.BOLD, 14)); messageLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); messageLabel.addMouseListener(new java.awt.event.MouseAdapter() { + @Override public void mouseClicked(java.awt.event.MouseEvent e) { if (e.getClickCount() > 1){ // System.out.println("debug profile is "+model.getProfile().getName()); @@ -690,7 +723,7 @@ public void mouseClicked(java.awt.event.MouseEvent e) { /** * This method initializes exitPanel - * + * * @return javax.swing.JPanel */ private JPanel getExitPanel() { @@ -708,7 +741,7 @@ private JPanel getExitPanel() { /** * This method initializes exitButton - * + * * @return javax.swing.JButton */ private JButton getExitButton() { @@ -718,6 +751,7 @@ private JButton getExitButton() { exitButton.setMnemonic(KeyEvent.VK_X); exitButton.setToolTipText("Click here to Shutdown PC^2"); exitButton.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent e) { promptAndExit(); } @@ -729,13 +763,14 @@ public void actionPerformed(java.awt.event.ActionEvent e) { private void showMessage(final String string) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(string); } }); } - + private void updateProfileLabel() { int numberProfiles = model.getProfiles().length; @@ -748,6 +783,7 @@ private void updateProfileLabel() { final String message = s; SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { messageLabel.setText(message); messageLabel.setToolTipText("Version "+versionInfo.getVersionNumber()+" (Build "+versionInfo.getBuildNumber()+")"); @@ -755,35 +791,39 @@ public void run() { }); } - + /** - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ - + // $HeadURL$ protected class ProfileListenerImplementation implements IProfileListener { + @Override public void profileAdded(ProfileEvent event) { updateProfileLabel(); } + @Override public void profileChanged(ProfileEvent event) { updateProfileLabel(); } + @Override public void profileRemoved(ProfileEvent event) { // ignore - + } + @Override public void profileRefreshAll(ProfileEvent profileEvent) { updateProfileLabel(); } } - + } // @jve:decl-index=0:visual-constraint="10,10" diff --git a/src/edu/csus/ecs/pc2/ui/team/QuickSubmitter.java b/src/edu/csus/ecs/pc2/ui/team/QuickSubmitter.java index 47febe482..409f1d960 100644 --- a/src/edu/csus/ecs/pc2/ui/team/QuickSubmitter.java +++ b/src/edu/csus/ecs/pc2/ui/team/QuickSubmitter.java @@ -1,4 +1,4 @@ -// Copyright (C) 1989-2023 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. +// Copyright (C) 1989-2024 PC2 Development Team: John Clevenger, Douglas Lane, Samir Ashoo, and Troy Boudreau. package edu.csus.ecs.pc2.ui.team; import java.io.File; @@ -13,17 +13,20 @@ import edu.csus.ecs.pc2.core.model.Language; import edu.csus.ecs.pc2.core.model.Problem; import edu.csus.ecs.pc2.imports.ccs.IContestLoader; +import edu.csus.ecs.pc2.list.SubmissionSample; import edu.csus.ecs.pc2.ui.UIPlugin; /** * Submit runs from CDP submissions/. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class QuickSubmitter implements UIPlugin { private static final long serialVersionUID = 7640178138750506786L; + private static String DEFAULT_SUBMISSION_TYPE = "unknown"; + private IInternalController controller; private IInternalContest contest; @@ -32,7 +35,7 @@ public class QuickSubmitter implements UIPlugin { /** * Returns list of all files under dir path. - * + * * @param dir * @return */ @@ -69,9 +72,9 @@ public void setContestAndController(IInternalContest inContest, IInternalControl /** * Get all CDP submission filenames. - * + * * Example files under config\sumit\submissions - * + * * @param mycontest * @param cdpConfigdir CDP config/ dir * @return @@ -94,48 +97,90 @@ public List getAllCDPsubmissionFileNames(IInternalContest mycontest, Strin /** * submit runs for all input files. Guesses language and problem from file path and extension. - * + * * Will guess langauge and problem based on path - * + * * @see #guessLanguage(IInternalContest, String) * @see #guessProblem(IInternalContest, String) - * + * * @param a list of files to submit - * @return count of files sucessfully submitted/added. + * @return list of successfully submitted samples. */ - public int sendSubmissions(List filesToSubmit) { + public List sendSubmissions(List filesToSubmit) { + + List subList = new ArrayList(); + SubmissionSample sub; - int numberSubmitted = 0; - for (File file : filesToSubmit) { - try { - - Language language = LanguageUtilities.guessLanguage(getContest(), file.getAbsolutePath()); - if (language == null) { - String ext = LanguageUtilities.getExtension(file.getAbsolutePath()); - log.log(Level.WARNING, "Cannot identify language for ext= " + ext + " = Can not send submission for file " + file.getAbsolutePath()); - } else { - Problem problem = guessProblem(getContest(), file.getAbsolutePath()); - try { - controller.submitJudgeRun(problem, language, file.getAbsolutePath(), null); - log.log(Level.INFO, "submitted run with language " + language + " and problem " + problem); - numberSubmitted++; - } catch (Exception e) { - log.log(Level.SEVERE, "problem sending run for file " + file.getAbsolutePath() + " " + e.getMessage(), e); - } + sub = sendSubmission(file); + if(sub == null) { + break; + } + subList.add(sub); + } + + return subList; + } + + /** + * submit a single run + * + * Will guess langauge and problem based on path + * + * @see #guessLanguage(IInternalContest, String) + * @see #guessProblem(IInternalContest, String) + * + * @param File object to submit + * @return the SubmissionSample if it was submitted, null otherwise + */ + public SubmissionSample sendSubmission(File file) { + return(sendSubmission(file, false)); + } + + /** + * submit a single run + * + * Will guess langauge and problem based on path + * + * @see #guessLanguage(IInternalContest, String) + * @see #guessProblem(IInternalContest, String) + * + * @param File object to submit + * @param Boolean overrideStopOnFailure - to override iff a problem has stop on failiure set. + * @return the SubmissionSample if it was submitted, null otherwise + */ + public SubmissionSample sendSubmission(File file, boolean overrideStopOnFailure) { + + SubmissionSample subResult = null; + String filePath = file.getAbsolutePath(); + try { + + Language language = LanguageUtilities.guessLanguage(getContest(), filePath); + if (language == null) { + String ext = LanguageUtilities.getExtension(file.getAbsolutePath()); + log.log(Level.WARNING, "Cannot identify language for ext= " + ext + " = Can not send submission for file " + filePath); + } else { + Problem problem = guessProblem(getContest(), filePath); + try { + controller.submitJudgeRun(problem, language, filePath, null, overrideStopOnFailure); + log.log(Level.INFO, "submitted run with language " + language + " and problem " + problem + " source: " + filePath); + subResult = new SubmissionSample(problem.getShortName(), problem.getElementId(), + language.getDisplayName(), language.getElementId(), guessSubmissionType(file.getParent()), file); + } catch (Exception e) { + log.log(Level.SEVERE, "problem sending run for file " + filePath + " " + e.getMessage(), e); } - } catch (Exception e) { - e.printStackTrace(); - log.log(Level.SEVERE, "problem sending run for file " + file.getAbsolutePath() + " " + e.getMessage(), e); } + } catch (Exception e) { + e.printStackTrace(); + log.log(Level.SEVERE, "problem sending run for file " + filePath + " " + e.getMessage(), e); } - - return numberSubmitted; + + return subResult; } /** * Guess problem based on short problem name found in path. - * + * * @param contest2 * @param absolutePath * @return @@ -150,6 +195,25 @@ private Problem guessProblem(IInternalContest contest2, String absolutePath) { return null; } + /** + * Determine type of judge's submission based on the folder after "submissions/" + * + * @param filePath File to check + * @return String which is the type + */ + private String guessSubmissionType(String filePath) { + + int iSub = filePath.lastIndexOf(File.separator); + String retType; + + if(iSub != -1) { + retType = filePath.substring(iSub+1); + } else { + retType = DEFAULT_SUBMISSION_TYPE; + } + return(retType); + } + public IInternalContest getContest() { return contest; } @@ -161,19 +225,19 @@ public String getPluginTitle() { /** * List of files that match filter. - * + * * @param files * @param submitYesSamples output all AC/Yes sample file name * @param submitNoSamples output all non AC/Yes sample file name * @return list of files matching filter. */ public static List filterRuns(List files, boolean submitYesSamples, boolean submitNoSamples) { - + List outFiles = new ArrayList<>(); for (File file : files) { String path = file.getAbsolutePath().replace("\\", "/"); boolean isYes = path.indexOf("/accepted/") != -1; - + if (submitYesSamples && isYes){ outFiles.add(file); } @@ -181,7 +245,7 @@ public static List filterRuns(List files, boolean submitYesSamples, outFiles.add(file); } } - + return outFiles; } } diff --git a/test/edu/csus/ecs/pc2/core/log/NullController.java b/test/edu/csus/ecs/pc2/core/log/NullController.java index 052303695..38e460571 100644 --- a/test/edu/csus/ecs/pc2/core/log/NullController.java +++ b/test/edu/csus/ecs/pc2/core/log/NullController.java @@ -41,7 +41,7 @@ /** * A controller that does nothing. - * + * * @author pc2@ecs.csus.edu * @version $Id$ */ @@ -51,7 +51,7 @@ public class NullController implements IInternalController{ private Log log = null; - + protected NullController() { } @@ -60,106 +60,127 @@ public NullController(String logFilename) { log = new Log(logFilename); } + @Override public void updateSite(Site newSite) { // TODO Auto-generated method stub } + @Override public void updateRun(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles) { // TODO Auto-generated method stub } + @Override public void updateProfile(Profile profile) { // TODO Auto-generated method stub } + @Override public void updateProblem(Problem problem, ProblemDataFiles problemDataFiles) { // TODO Auto-generated method stub } + @Override public void updateProblem(Problem problem) { // TODO Auto-generated method stub } + @Override public void updateLanguage(Language language) { // TODO Auto-generated method stub } + @Override public void updateJudgement(Judgement newJudgement) { // TODO Auto-generated method stub } + @Override public void updateGroup(Group group) { // TODO Auto-generated method stub } + @Override public void updateFinalizeData(FinalizeData data) { // TODO Auto-generated method stub } + @Override public void updateContestTime(ContestTime newContestTime) { // TODO Auto-generated method stub } + @Override public void updateContestInformation(ContestInformation contestInformation) { // TODO Auto-generated method stub } + @Override public void updateContestController(IInternalContest inContest, IInternalController inController) { // TODO Auto-generated method stub } + @Override public void updateClientSettings(ClientSettings clientSettings) { // TODO Auto-generated method stub } + @Override public void updateCategory(Category newCategory) { // TODO Auto-generated method stub } + @Override public void updateCategories(Category[] categories) { // TODO Auto-generated method stub } + @Override public void updateBalloonSettings(BalloonSettings newBalloonSettings) { // TODO Auto-generated method stub } + @Override public void updateAccounts(Account[] account) { // TODO Auto-generated method stub } + @Override public void updateAccount(Account account) { // TODO Auto-generated method stub } + @Override public void syncProfileSubmissions(Profile profile) { // TODO Auto-generated method stub } + @Override public void switchProfile(Profile currentProfile, Profile switchToProfile, String contestPassword) { // TODO Auto-generated method stub } + @Override public void submitRunJudgement(Run run, JudgementRecord judgementRecord, RunResultFiles runResultFiles) { // TODO Auto-generated method stub @@ -170,476 +191,581 @@ public void submitJudgeRun(Problem problem, Language language, String mainFileNa } + @Override public void submitClarificationAnswer(Clarification clarification) { // TODO Auto-generated method stub } + @Override public void submitClarification(Problem problem, String question) { // TODO Auto-generated method stub } + @Override public void stopContest(int inSiteNumber) { // TODO Auto-generated method stub } + @Override public void stopAllContestTimes() { // TODO Auto-generated method stub } + @Override public void startPlayback(PlaybackInfo playbackInfo) { // TODO Auto-generated method stub } + @Override public void startMainUI(ClientId clientId) { // TODO Auto-generated method stub } + @Override public ILogWindow startLogWindow(IInternalContest contest) { getLog().log(Level.WARNING, "Attempted to use startLogWindow ", new Exception("Attempted to use startLogWindow")); return null; } + @Override public void startContest(int inSiteNumber) { // TODO Auto-generated method stub } + @Override public void startAllContestTimes() { // TODO Auto-generated method stub } + @Override public void start(String[] stringArray) { // TODO Auto-generated method stub } + @Override public void shutdownTransport() { // TODO Auto-generated method stub } + @Override public void shutdownServer(ClientId requestor, int siteNumber) { // TODO Auto-generated method stub } + @Override public void shutdownServer(ClientId requestor) { // TODO Auto-generated method stub } + @Override public void shutdownRemoteServers(ClientId requestor) { // TODO Auto-generated method stub } + @Override public void showLogWindow(boolean showWindow) { // TODO Auto-generated method stub } + @Override public void setUsingGUI(boolean usingGUI) { // TODO Auto-generated method stub } + @Override public void setSiteNumber(int i) { // TODO Auto-generated method stub } + @Override public void setSecurityLevel(int securityLevel) { // TODO Auto-generated method stub } + @Override public void setJudgementList(Judgement[] judgementList) { // TODO Auto-generated method stub } + @Override public void setContestTime(ContestTime contestTime) { // TODO Auto-generated method stub } + @Override public void setContest(IInternalContest newContest) { // TODO Auto-generated method stub } + @Override public void setClientAutoShutdown(boolean clientAutoShutdown) { // TODO Auto-generated method stub } + @Override public void sendValidatingMessage(Run run) { // TODO Auto-generated method stub } + @Override public void sendToTeams(Packet packet) { // TODO Auto-generated method stub } + @Override public void sendToSpectators(Packet packet) { // TODO Auto-generated method stub } + @Override public void sendToServers(Packet packet) { // TODO Auto-generated method stub } + @Override public void sendToScoreboards(Packet packet) { // TODO Auto-generated method stub } + @Override public void sendToRemoteServer(int siteNumber, Packet packet) { // TODO Auto-generated method stub } + @Override public void sendToLocalServer(Packet packet) { // TODO Auto-generated method stub } + @Override public void sendToJudges(Packet packet) { // TODO Auto-generated method stub } + @Override public void sendToClient(Packet packet) { // TODO Auto-generated method stub } + @Override public void sendToAdministrators(Packet packet) { // TODO Auto-generated method stub } + @Override public void sendShutdownSite(int siteNumber) { // TODO Auto-generated method stub } + @Override public void sendShutdownAllSites() { // TODO Auto-generated method stub } + @Override public void sendServerLoginRequest(int inSiteNumber) throws Exception { // TODO Auto-generated method stub } + @Override public void sendSecurityMessage(String event, String message, ContestSecurityException contestSecurityException) { // TODO Auto-generated method stub } + @Override public void sendExecutingMessage(Run run) { // TODO Auto-generated method stub } + @Override public void sendCompilingMessage(Run run) { // TODO Auto-generated method stub } + @Override public void resetContest(ClientId clientResettingContest, boolean eraseProblems, boolean eraseLanguages) { // TODO Auto-generated method stub } + @Override public void requestChangePassword(String oldPassword, String newPassword) { // TODO Auto-generated method stub } + @Override public void removePacketListener(IPacketListener packetListener) { // TODO Auto-generated method stub } + @Override public void removeLogin(ClientId clientId) { // TODO Auto-generated method stub } + @Override public void removeJudgement(Judgement judgement) { // TODO Auto-generated method stub } + @Override public void removeConnection(ConnectionHandlerID connectionHandlerID) { // TODO Auto-generated method stub } + @Override public void register(UIPlugin plugin) { // TODO Auto-generated method stub } + @Override public void outgoingPacket(Packet packet) { // TODO Auto-generated method stub } + @Override public void logoffUser(ClientId clientId) { // TODO Auto-generated method stub } + @Override public void login(String loginName, String password) { // TODO Auto-generated method stub } + @Override public void logWarning(String string, Exception e) { // TODO Auto-generated method stub } + @Override public boolean isUsingGUI() { return false; } + @Override public boolean isLogWindowVisible() { return false; } + @Override public boolean isClientAutoShutdown() { return false; } + @Override public void initializeStorage(IStorage storage) { // TODO Auto-generated method stub } + @Override public void initializeServer(IInternalContest contest) throws IOException, ClassNotFoundException, FileSecurityException { // TODO Auto-generated method stub } + @Override public void incomingPacket(Packet packet) { // TODO Auto-generated method stub } + @Override public int getSecurityLevel() { return 0; } + @Override public ProblemDataFiles getProblemDataFiles(Problem problem) { return null; } + @Override public int getPortContacted() { return 0; } + @Override public UIPlugin[] getPluginList() { return null; } + @Override public Log getLog() { return log; } + @Override public String getHostContacted() { return null; } + @Override public void generateNewAccounts(String clientTypeName, int count, int startNumber, boolean active) { // TODO Auto-generated method stub } + @Override public void generateNewAccounts(String clientTypeName, int siteNumber, int count, int startNumber, boolean active) { // TODO Auto-generated method stub } + @Override public void forceConnectionDrop(ConnectionHandlerID connectionHandlerID) { // TODO Auto-generated method stub } + @Override public void fetchRun(Run run) throws IOException, ClassNotFoundException, FileSecurityException { // TODO Auto-generated method stub } + @Override public void cloneProfile(Profile profile, ProfileCloneSettings settings, boolean switchNow) { // TODO Auto-generated method stub } + @Override public IInternalContest clientLogin(IInternalContest internalContest, String loginName, String password) throws Exception { return null; } + @Override public void checkOutRun(Run run, boolean readOnly, boolean computerJudge) { // TODO Auto-generated method stub } + @Override public void checkOutRejudgeRun(Run theRun) { // TODO Auto-generated method stub } + @Override public void checkOutClarification(Clarification clarification, boolean readOnly) { // TODO Auto-generated method stub } + @Override public void cancelRun(Run run) { // TODO Auto-generated method stub } + @Override public void cancelClarification(Clarification clarification) { // TODO Auto-generated method stub } + @Override public void addProblem(Problem problem) { // TODO Auto-generated method stub } + @Override public void addPacketListener(IPacketListener packetListener) { // TODO Auto-generated method stub } + @Override public void addNewSite(Site site) { // TODO Auto-generated method stub } + @Override public void addNewProblem(Problem[] problem, ProblemDataFiles[] problemDataFiles) { // TODO Auto-generated method stub } + @Override public void addNewProblem(Problem problem, ProblemDataFiles problemDataFiles) { // TODO Auto-generated method stub } + @Override public void addNewLanguage(Language language) { // TODO Auto-generated method stub } + @Override public void addNewJudgement(Judgement judgement) { // TODO Auto-generated method stub } + @Override public void addNewGroup(Group group) { // TODO Auto-generated method stub } + @Override public void addNewClientSettings(ClientSettings newClientSettings) { // TODO Auto-generated method stub } + @Override public void addNewCategory(Category newCategory) { // TODO Auto-generated method stub } + @Override public void addNewBalloonSettings(BalloonSettings newBalloonSettings) { // TODO Auto-generated method stub } + @Override public void addNewAccounts(Account[] account) { // TODO Auto-generated method stub } + @Override public void addNewAccount(Account account) { // TODO Auto-generated method stub } + @Override public void submitJudgeRun(Problem problem, Language language, String filename, SerializedFile[] otherFiles) throws Exception { // TODO Auto-generated method stub - + } + @Override public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] auxFileList, long overrideSubmissionTimeMS, long overrideRunId) throws Exception { // TODO Auto-generated method stub - + + } + @Override + public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, boolean overrideStopOnFailure) throws Exception { + + } + + @Override + public void submitJudgeRun(Problem problem, Language language, String mainFileName, SerializedFile[] otherFiles, + long overrideSubmissionTimeMS, long overrideRunId, boolean overrideStopOnFailure) throws Exception { + } + @Override + public void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, + long overrideSubmissionTimeMS, long overrideRunId, boolean overrideStopOnFailure) throws Exception { + } + + @Override public void sendRunToSubmissionInterface(Run run, RunFiles runFiles) { // TODO Auto-generated method stub - + } + @Override public void autoRegister(String teamInformation) { // TODO Auto-generated method stub - + } + @Override public void sendToJudgesAndOthers(Packet packet, boolean sendToServers) { // TODO Auto-generated method stub - + } @Override public void setConnectionManager(ITransportManager connectionManager) { // TODO Auto-generated method stub - + } @Override public void addNewLanguages(Language[] languages) { - + } @Override public void updateLanguages(Language[] languages) { - + } @Override public void addNewGroups(Group[] groups) { - + } @Override public void updateGroups(Group[] groups) { - + } @Override public void updateAutoStartInformation(IInternalContest aContest, IInternalController aController) { // TODO Auto-generated method stub - + } @Override @@ -651,7 +777,7 @@ public IInternalContest getContest() { @Override public void submitRun(ClientId submitter, Problem problem, Language language, SerializedFile mainSubmissionFile, SerializedFile[] additionalFiles, long overrideTimeMS, long overrideRunId) { // TODO Auto-generated method stub - + } @Override @@ -664,7 +790,7 @@ public void submitRun(ClientId submitter, Problem problem, Language language, St @Override public void submitJudgeRun(Problem problem, Language language, SerializedFile mainFile, SerializedFile[] otherFiles, long overrideSubmissionTimeMS, long overrideRunId) throws Exception { // TODO Auto-generated method stub - + } @Override diff --git a/test/edu/csus/ecs/pc2/shadow/RemoteRunSubmitterTest.java b/test/edu/csus/ecs/pc2/shadow/RemoteRunSubmitterTest.java index 2bc26f96b..50faf365b 100644 --- a/test/edu/csus/ecs/pc2/shadow/RemoteRunSubmitterTest.java +++ b/test/edu/csus/ecs/pc2/shadow/RemoteRunSubmitterTest.java @@ -27,80 +27,80 @@ import edu.csus.ecs.pc2.core.util.AbstractTestCase; public class RemoteRunSubmitterTest extends AbstractTestCase{ - - + + /** - * Test submitrun + * Test submitrun * @throws Exception */ public void testOneSubmit() throws Exception { - + String contestDBdir = getOutputDataDirectory(getName()); - + ensureDirectory(contestDBdir); IStorage storage = new FileStorage(contestDBdir); - + SampleContest sample = new SampleContest(); IInternalContest contest = sample.createStandardContest(); - + contest.setStorage(storage); - + contest.generateNewAccounts(Type.FEEDER.toString(), 2, true); Account feeder = getRandomAccount(contest, Type.FEEDER); setCCSMode (contest, feeder); - + assertTrue("Expecting shadow for feeder ",contest.isAllowed(feeder.getClientId(), Permission.Type.SHADOW_PROXY_TEAM)); assertTrue("Not CCS Test ", contest.getContestInformation().isCcsTestMode()); - + IInternalController controller = new MockController(); controller.initializeServer(contest); - + Account[] teams = getTeamAccounts(contest); Arrays.sort(teams, new AccountComparator()); assertEquals("Number of teams", 120, teams.length); - + assertEquals("Expected languages in contest ", 6, contest.getLanguages().length); Account account = getRandomAccount(contest, Type.TEAM); ClientId team = account.getClientId(); Language language = getRandomLanguage(contest); Problem problem = getRandomProblem(contest); - + assertNotNull("Language should not be null ",language); assertNotNull("Language name should not be null ",language.getDisplayName()); - + assertNotNull(feeder); contest.setClientId(feeder.getClientId()); RemoteRunSubmitter sub = new RemoteRunSubmitter(controller); - + // sub.submitRun(account.getClientId(), problemName, languageName, mainFile, auxFiles, overrideTimeMS, overrideRunId); - + ClientId submitter = team; SerializedFile mainSubmissionFile = new SerializedFile(getSamplesSourceFilename(SUMIT_SOURCE_FILENAME)); SerializedFile[] additionalFiles = new SerializedFile[0]; - + long overrideTimeMS = 300; long overrideRunId = 44; controller.submitRun(submitter, problem, language, mainSubmissionFile, additionalFiles, overrideTimeMS, overrideRunId); - + assertEquals("Expecting number of runs ", 1, contest.getRuns().length); printruns(contest); - + String[] src_lines = Utilities.loadFile(getSamplesSourceFilename(SUMIT_SOURCE_FILENAME)); String source = String.join("", src_lines); - + String base64String = Base64.getEncoder().encodeToString(source.getBytes()); IFile mainFile = new IFileImpl("sumit.java", base64String); - + List auxFiles = new ArrayList(); - + sub.submitRun(submitter.getName().toLowerCase(), problem.getShortName(), language.getID(), mainFile, auxFiles, overrideTimeMS, overrideRunId); - + assertEquals("Expecting number of runs ", 2, contest.getRuns().length); - + printruns(contest); - + } /** @@ -109,27 +109,27 @@ public void testOneSubmit() throws Exception { * @param account */ private void setCCSMode(IInternalContest contest, Account account) { - + account.addPermission(Permission.Type.SHADOW_PROXY_TEAM); contest.updateAccount(account); - + ContestInformation contestInformation = contest.getContestInformation(); contestInformation.setCcsTestMode(true); contest.updateContestInformation(contestInformation); } private void printruns(IInternalContest contest) { - + Run[] runs = contest.getRuns(); System.out.println(""); System.out.println("There are "+runs.length+" runs"); for (Run run : runs) { System.out.println("Run = "+run.getNumber()+" is "+run); } - - + + } - + } diff --git a/test/edu/csus/ecs/pc2/ui/team/QuickSubmitterTest.java b/test/edu/csus/ecs/pc2/ui/team/QuickSubmitterTest.java index 1022dbef3..92dffa843 100644 --- a/test/edu/csus/ecs/pc2/ui/team/QuickSubmitterTest.java +++ b/test/edu/csus/ecs/pc2/ui/team/QuickSubmitterTest.java @@ -16,10 +16,11 @@ import edu.csus.ecs.pc2.core.util.AbstractTestCase; import edu.csus.ecs.pc2.imports.ccs.ContestSnakeYAMLLoader; import edu.csus.ecs.pc2.imports.ccs.IContestLoader; +import edu.csus.ecs.pc2.list.SubmissionSample; /** * Unit test. - * + * * @author Douglas A. Lane, PC^2 Team, pc2@ecs.csus.edu */ public class QuickSubmitterTest extends AbstractTestCase { @@ -123,7 +124,7 @@ public void testfilter() throws Exception { /** * Test QuickSubmitter send/add submissions. - * + * * @throws Exception */ public void testsendSubmissions() throws Exception { @@ -154,7 +155,13 @@ public void testsendSubmissions() throws Exception { List files = submitter.getAllCDPsubmissionFileNames(contest, cdpConfig); assertEquals(expectedFiles, files.size()); - int totSubmit = submitter.sendSubmissions(files); + List subList = submitter.sendSubmissions(files); + int totSubmit; + if(subList == null) { + totSubmit = 0; + } else { + totSubmit = subList.size(); + } assertEquals(expectedFiles, totSubmit); }