1+ package fi .helsinki .cs .tmc .cli .command .admin ;
2+
3+ import fi .helsinki .cs .tmc .cli .core .AbstractCommand ;
4+ import fi .helsinki .cs .tmc .cli .core .CliContext ;
5+ import fi .helsinki .cs .tmc .cli .core .Command ;
6+ import fi .helsinki .cs .tmc .cli .io .Io ;
7+
8+ import fi .helsinki .cs .tmc .langs .LanguagePlugin ;
9+ import fi .helsinki .cs .tmc .langs .abstraction .ValidationResult ;
10+ import fi .helsinki .cs .tmc .langs .domain .ExerciseDesc ;
11+ import fi .helsinki .cs .tmc .langs .domain .ExercisePackagingConfiguration ;
12+ import fi .helsinki .cs .tmc .langs .domain .Filer ;
13+ import fi .helsinki .cs .tmc .langs .domain .FilterFileTreeVisitor ;
14+ import fi .helsinki .cs .tmc .langs .domain .GeneralDirectorySkipper ;
15+ import fi .helsinki .cs .tmc .langs .domain .NoLanguagePluginFoundException ;
16+ import fi .helsinki .cs .tmc .langs .domain .RunResult ;
17+ import fi .helsinki .cs .tmc .langs .util .ProjectType ;
18+ import fi .helsinki .cs .tmc .langs .util .TaskExecutor ;
19+
20+ import com .google .common .base .Optional ;
21+ import com .google .gson .Gson ;
22+ import org .apache .commons .cli .CommandLine ;
23+ import org .apache .commons .cli .Options ;
24+ import org .slf4j .Logger ;
25+ import org .slf4j .LoggerFactory ;
26+
27+ import java .io .FileWriter ;
28+ import java .io .IOException ;
29+ import java .nio .file .FileVisitResult ;
30+ import java .nio .file .Path ;
31+ import java .nio .file .Paths ;
32+ import java .util .HashMap ;
33+ import java .util .HashSet ;
34+ import java .util .Locale ;
35+ import java .util .Map ;
36+ import java .util .Set ;
37+
38+ /*TODO split this command to multiple admin commands */
39+ @ Command (name = "admin" , desc = "The admin command" )
40+ public class AdminCommand extends AbstractCommand {
41+
42+ private static final Logger logger = LoggerFactory .getLogger (AdminCommand .class );
43+ private Io io ;
44+
45+ private TaskExecutor executor ;
46+ private Path exercisePath ;
47+ private Path outputPath ;
48+ private Locale locale ;
49+
50+ @ Override
51+ public void getOptions (Options options ) {
52+ options .addOption ("e" , "exercise" , true , "Path to exercise" );
53+ options .addOption ("o" , "output" , true , "Output path for the sub-command" );
54+ options .addOption ("l" , "locale" , true , "Locale of the exercise?" );
55+ }
56+
57+ @ Override
58+ public void run (CliContext context , CommandLine args ) {
59+ this .io = context .getIo ();
60+ String [] stringArguments = args .getArgs ();
61+
62+ if (stringArguments .length == 0 ) {
63+ io .errorln ("You have to give some sub-command." );
64+ printUsage (context );
65+ return ;
66+ }
67+
68+ if (!context .loadBackendWithoutLogin ()) {
69+ return ;
70+ }
71+
72+ this .executor = context .getTmcLangs ();
73+ String subCommand = stringArguments [0 ];
74+ if (args .hasOption ("exercise" )) {
75+ this .exercisePath = Paths .get (args .getOptionValue ("exercise" ));
76+ }
77+ if (args .hasOption ("output" )) {
78+ this .outputPath = Paths .get (args .getOptionValue ("output" ));
79+ }
80+ if (args .hasOption ("locale" )) {
81+ this .locale = new Locale (args .getOptionValue ("locale" ));
82+ }
83+ runSubCommand (subCommand );
84+ }
85+
86+ private void runSubCommand (String subCommand ) {
87+ switch (subCommand ) {
88+ case "help" :
89+ io .errorln ("Not implemented!" );
90+ break ;
91+ case "checkstyle" :
92+ runCheckCodeStyle ();
93+ break ;
94+ case "scan-exercise" :
95+ runScanExercise ();
96+ break ;
97+ case "find-exercises" :
98+ runFindExercises ();
99+ break ;
100+ case "run-tests" :
101+ runTests ();
102+ break ;
103+ case "prepare-stubs" :
104+ runPrepareStubs ();
105+ break ;
106+ case "prepare-solutions" :
107+ runPrepareSolutions ();
108+ break ;
109+ case "get-exercise-packaging-configuration" :
110+ runGetExercisePackagingConfiguration ();
111+ break ;
112+ case "clean" :
113+ runClean ();
114+ break ;
115+ default :
116+ break ;
117+ }
118+ }
119+
120+ private void runCheckCodeStyle () {
121+ if (this .exercisePath == null || this .outputPath == null || this .locale == null ) {
122+ io .errorln ("Checkstyle command expects exercise path, output path and locale." );
123+ return ;
124+ }
125+ ValidationResult validationResult ;
126+ try {
127+ validationResult =
128+ executor .runCheckCodeStyle (this .exercisePath , this .locale );
129+ } catch (NoLanguagePluginFoundException e ) {
130+ logger .error (
131+ "No suitable language plugin for project at {}" , this .exercisePath , e );
132+ io .errorln (
133+ "ERROR: Could not find suitable language plugin for the given exercise "
134+ + "path." );
135+ return ;
136+ }
137+
138+ try {
139+ writeObjectIntoJsonFormat (validationResult , this .outputPath );
140+ io .println ("Codestyle report can be found at " + this .outputPath );
141+ } catch (IOException e ) {
142+ logger .error ("Could not write result into {}" , this .outputPath , e );
143+ io .errorln ("ERROR: Could not write the results to the given file." );
144+ }
145+ }
146+
147+ private void runScanExercise () {
148+ if (this .exercisePath == null || this .outputPath == null ) {
149+ io .errorln ("ScanExercise command expects exercise path and output path." );
150+ return ;
151+ }
152+ String exerciseName = this .exercisePath .toFile ().getName ();
153+ Optional <ExerciseDesc > exerciseDesc ;
154+ try {
155+ exerciseDesc = executor .scanExercise (this .exercisePath , exerciseName );
156+
157+ if (exerciseDesc == null || !exerciseDesc .isPresent ()) {
158+ logger .error ("Absent exercise description after running scanExercise" );
159+ io .errorln ("ERROR: Could not scan the exercises." );
160+ return ;
161+ }
162+ } catch (NoLanguagePluginFoundException e ) {
163+ logger .error (
164+ "No suitable language plugin for project at {}" , this .exercisePath , e );
165+ io .errorln (
166+ "ERROR: Could not find suitable language plugin for the given "
167+ + "exercise path." );
168+ return ;
169+ }
170+
171+ try {
172+ writeObjectIntoJsonFormat (exerciseDesc .get (), this .outputPath );
173+ io .println (
174+ "Exercises scanned successfully, results can be found in "
175+ + this .outputPath );
176+ } catch (IOException e ) {
177+ logger .error ("Could not write output to {}" , this .outputPath , e );
178+ io .errorln ("ERROR: Could not write the results to the given file." );
179+ }
180+ }
181+
182+ private void runFindExercises () {
183+ if (this .exercisePath == null || this .outputPath == null ) {
184+ io .errorln ("FindExercises command expects exercise path and output path." );
185+ return ;
186+ }
187+ Path clonePath = this .exercisePath ;
188+ final Set <String > exercises = new HashSet <>();
189+ Filer exerciseMatchingFiler =
190+ new Filer () {
191+
192+ @ Override
193+ public FileVisitResult decideOnDirectory (Path directory ) {
194+ if (executor .isExerciseRootDirectory (directory )) {
195+ exercises .add (directory .toString ());
196+ return FileVisitResult .SKIP_SUBTREE ;
197+ }
198+ return FileVisitResult .CONTINUE ;
199+ }
200+
201+ @ Override
202+ public void visitFile (Path source , Path relativePath ) {}
203+ };
204+ new FilterFileTreeVisitor ()
205+ .addSkipper (new GeneralDirectorySkipper ())
206+ .setClonePath (clonePath )
207+ .setStartPath (clonePath )
208+ .setFiler (exerciseMatchingFiler )
209+ .traverse ();
210+
211+ try {
212+ writeObjectIntoJsonFormat (exercises , this .outputPath );
213+ io .println ("Results can be found in " + this .outputPath );
214+ } catch (IOException e ) {
215+ logger .error ("Could not write output to {}" , this .outputPath , e );
216+ io .errorln ("ERROR: Could not write the results to the given file." );
217+ }
218+ }
219+
220+ private void runTests () {
221+ if (this .exercisePath == null || this .outputPath == null ) {
222+ io .errorln ("RunTests command expects exercise path and output path." );
223+ return ;
224+ }
225+ RunResult runResult ;
226+ try {
227+ runResult = executor .runTests (this .exercisePath );
228+ } catch (NoLanguagePluginFoundException e ) {
229+ logger .error (
230+ "No suitable language plugin for project at {}" , this .exercisePath , e );
231+ io .errorln (
232+ "ERROR: Could not find suitable language plugin for the given "
233+ + "exercise path." );
234+ return ;
235+ }
236+
237+ try {
238+ writeObjectIntoJsonFormat (runResult , this .outputPath );
239+ io .println ("Test results can be found in " + this .outputPath );
240+ } catch (IOException e ) {
241+ logger .error ("Could not write output to {}" , this .outputPath , e );
242+ io .errorln ("ERROR: Could not write the results to the given file." );
243+ }
244+ }
245+
246+ private void runPrepareStubs () {
247+ if (this .exercisePath == null || this .outputPath == null || this .locale == null ) {
248+ io .errorln ("RunTests command expects exercise path, output path and locale." );
249+ return ;
250+ }
251+ try {
252+ executor .prepareStubs (
253+ findExerciseDirectoriesAndGetLanguagePlugins (),
254+ this .exercisePath ,
255+ this .outputPath );
256+ } catch (NoLanguagePluginFoundException e ) {
257+ logger .error (
258+ "No suitable language plugin for project at {}" , this .exercisePath , e );
259+ io .errorln (
260+ "ERROR: Could not find suitable language plugin for the given "
261+ + "exercise path." );
262+ }
263+ }
264+
265+ private void runClean () {
266+ try {
267+ executor .clean (this .exercisePath );
268+ } catch (NoLanguagePluginFoundException e ) {
269+ logger .error (
270+ "No suitable language plugin for project at {}" , this .exercisePath , e );
271+ io .errorln (
272+ "ERROR: Could not find suitable language plugin for the given "
273+ + "exercise path." );
274+ }
275+ }
276+
277+ private void runPrepareSolutions () {
278+ try {
279+ executor .prepareSolutions (
280+ findExerciseDirectoriesAndGetLanguagePlugins (),
281+ this .exercisePath ,
282+ this .outputPath );
283+ } catch (NoLanguagePluginFoundException e ) {
284+ logger .error (
285+ "No suitable language plugin for project at {}" , this .exercisePath , e );
286+ io .errorln (
287+ "ERROR: Could not find suitable language plugin for the given "
288+ + "exercise path." );
289+ }
290+ }
291+
292+ private void runGetExercisePackagingConfiguration () {
293+ ExercisePackagingConfiguration configuration ;
294+ try {
295+ configuration = executor .getExercisePackagingConfiguration (this .exercisePath );
296+ } catch (NoLanguagePluginFoundException e ) {
297+ logger .error (
298+ "No suitable language plugin for project at {}" , this .exercisePath , e );
299+ io .errorln (
300+ "ERROR: Could not find suitable language plugin for the given "
301+ + "exercise path." );
302+ return ;
303+ }
304+
305+ try {
306+ writeObjectIntoJsonFormat (configuration , this .outputPath );
307+ io .println ("Results can be found in " + this .outputPath );
308+ } catch (IOException e ) {
309+ logger .error ("Could not write output to {}" , this .outputPath , e );
310+ io .errorln ("ERROR: Could not write the results to the given file." );
311+ }
312+ }
313+
314+ private Map <Path , LanguagePlugin > findExerciseDirectoriesAndGetLanguagePlugins () {
315+ final Map <Path , LanguagePlugin > map = new HashMap <>();
316+ Filer exerciseMatchingFiler =
317+ new Filer () {
318+
319+ @ Override
320+ public FileVisitResult decideOnDirectory (Path directory ) {
321+ if (executor .isExerciseRootDirectory (directory )) {
322+ try {
323+ map .put (
324+ directory ,
325+ ProjectType .getProjectType (directory ).getLanguagePlugin ());
326+ } catch (NoLanguagePluginFoundException ex ) {
327+ throw new IllegalStateException (ex );
328+ }
329+ return FileVisitResult .SKIP_SUBTREE ;
330+ }
331+ return FileVisitResult .CONTINUE ;
332+ }
333+
334+ @ Override
335+ public void visitFile (Path source , Path relativePath ) {}
336+ };
337+ new FilterFileTreeVisitor ()
338+ .addSkipper (new GeneralDirectorySkipper ())
339+ .setClonePath (this .exercisePath )
340+ .setStartPath (this .exercisePath )
341+ .setFiler (exerciseMatchingFiler )
342+ .traverse ();
343+ return map ;
344+ }
345+
346+ private static void writeObjectIntoJsonFormat (Object obj , Path outputFile ) throws IOException {
347+ try (FileWriter writer = new FileWriter (outputFile .toAbsolutePath ().toFile ())) {
348+ writer .write (new Gson ().toJson (obj ));
349+ }
350+ }
351+ }
0 commit comments