├── koans ├── app │ ├── lib │ │ └── koans.jar │ ├── config │ │ ├── compilationcommands.properties │ │ ├── config.properties │ │ └── PathToEnlightenment.xml │ └── data │ │ └── src │ │ ├── beginner │ │ ├── AboutKoans.java │ │ ├── AboutConstructors.java │ │ ├── AboutArithmeticOperators.java │ │ ├── AboutMethodPreference.java │ │ ├── AboutVarArgs.java │ │ ├── AboutEquality.java │ │ ├── AboutEnums.java │ │ ├── AboutObjects.java │ │ ├── AboutArrays.java │ │ ├── AboutAssertions.java │ │ └── AboutInheritance.java │ │ ├── java8 │ │ ├── AboutLocalTime.java │ │ ├── AboutMultipleInheritance.java │ │ ├── AboutOptional.java │ │ ├── AboutDefaultMethods.java │ │ └── AboutLambdas.java │ │ ├── java7 │ │ ├── AboutStringsInSwitch.java │ │ ├── AboutDiamondOperator.java │ │ └── AboutRequireNotNull.java │ │ ├── advanced │ │ └── AboutMocks.java │ │ └── intermediate │ │ ├── AboutAutoboxing.java │ │ ├── AboutDates.java │ │ └── AboutComparison.java ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── build.gradle ├── src │ ├── beginner │ │ ├── AboutKoans.java │ │ ├── AboutConstructors.java │ │ ├── AboutArithmeticOperators.java │ │ ├── AboutMethodPreference.java │ │ ├── AboutVarArgs.java │ │ ├── AboutEquality.java │ │ ├── AboutEnums.java │ │ ├── AboutObjects.java │ │ ├── AboutArrays.java │ │ ├── AboutAssertions.java │ │ └── AboutInheritance.java │ ├── java8 │ │ ├── AboutLocalTime.java │ │ ├── AboutMultipleInheritance.java │ │ ├── AboutOptional.java │ │ ├── AboutDefaultMethods.java │ │ └── AboutLambdas.java │ ├── java7 │ │ ├── AboutStringsInSwitch.java │ │ ├── AboutDiamondOperator.java │ │ ├── AboutRequireNotNull.java │ │ └── AboutTryWithResources.java │ ├── advanced │ │ └── AboutMocks.java │ └── intermediate │ │ ├── AboutAutoboxing.java │ │ ├── AboutDates.java │ │ └── AboutComparison.java ├── README.md ├── run.bat ├── run.sh └── gradlew.bat ├── lib ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── sandwich │ │ │ ├── util │ │ │ ├── Builder.java │ │ │ ├── io │ │ │ │ ├── ui │ │ │ │ │ ├── ErrorPresenter.java │ │ │ │ │ └── DefaultErrorPresenter.java │ │ │ │ ├── FileListener.java │ │ │ │ ├── directories │ │ │ │ │ ├── ProductionDirectories.java │ │ │ │ │ └── DirectorySet.java │ │ │ │ ├── filecompiler │ │ │ │ │ ├── CompilationListener.java │ │ │ │ │ ├── CompilationFailureLogger.java │ │ │ │ │ └── CompilerConfig.java │ │ │ │ ├── FileAction.java │ │ │ │ ├── StreamUtils.java │ │ │ │ ├── ForEachFileAction.java │ │ │ │ ├── ExistingFileAction.java │ │ │ │ ├── FileUtils.java │ │ │ │ ├── CopyFileOperation.java │ │ │ │ ├── KoanSuiteCompilationListener.java │ │ │ │ ├── FileOperation.java │ │ │ │ ├── SourceAndDestinationFileAction.java │ │ │ │ ├── FileMonitorFactory.java │ │ │ │ ├── KoanFileCompileAndRunListener.java │ │ │ │ ├── FileMonitor.java │ │ │ │ └── DataFileHelper.java │ │ │ ├── ExceptionUtils.java │ │ │ ├── KoanComparator.java │ │ │ ├── Assert.java │ │ │ └── Strings.java │ │ │ └── koan │ │ │ ├── cmdline │ │ │ ├── behavior │ │ │ │ ├── ArgumentBehavior.java │ │ │ │ ├── Debug.java │ │ │ │ ├── Reset.java │ │ │ │ ├── AbstractArgumentBehavior.java │ │ │ │ ├── MethodArg.java │ │ │ │ ├── ClassArg.java │ │ │ │ ├── Backup.java │ │ │ │ ├── KoanFileCopying.java │ │ │ │ ├── Clear.java │ │ │ │ └── Help.java │ │ │ ├── CommandLineArgumentRunner.java │ │ │ ├── CommandLineArgument.java │ │ │ └── CommandLineArgumentBuilder.java │ │ │ ├── path │ │ │ └── xmltransformation │ │ │ │ ├── XmlToPathTransformer.java │ │ │ │ ├── XmlVariableDictionary.java │ │ │ │ ├── KoanElementAttributes.java │ │ │ │ └── RbVariableInjector.java │ │ │ ├── Koan.java │ │ │ ├── KoanIncompleteException.java │ │ │ ├── ui │ │ │ ├── SuitePresenter.java │ │ │ └── AbstractSuitePresenter.java │ │ │ ├── util │ │ │ └── ApplicationUtils.java │ │ │ ├── result │ │ │ └── KoanMethodResult.java │ │ │ ├── constant │ │ │ ├── KoanConstants.java │ │ │ └── ArgumentType.java │ │ │ ├── ApplicationSettings.java │ │ │ ├── KoanClassLoader.java │ │ │ ├── runner │ │ │ ├── AppLauncher.java │ │ │ └── KoanMethodRunner.java │ │ │ └── KoanMethod.java │ └── test │ │ └── java │ │ └── com │ │ └── sandwich │ │ ├── koan │ │ ├── suite │ │ │ ├── OnePassingKoanDifferentName.java │ │ │ ├── OneFailingKoan.java │ │ │ ├── OnePassingKoan.java │ │ │ ├── WrongExpectationOrderKoan.java │ │ │ ├── OneFailingKoanDifferentName.java │ │ │ ├── BlowUpOnLineTen.java │ │ │ ├── TwoFailingKoans.java │ │ │ └── BlowUpOnLineEleven.java │ │ ├── AnonymousInnerClassTest.java │ │ ├── constant │ │ │ └── ArgumentTypeTest.java │ │ ├── KoansResultTest.java │ │ ├── path │ │ │ ├── xmltransformation │ │ │ │ └── FakeXmlToPathTransformer.java │ │ │ ├── PathToEnlightenmentTest.java │ │ │ └── RbVariableInjectorTest.java │ │ ├── LocaleSwitchingTestCase.java │ │ ├── cmdline │ │ │ └── CommandLineArgumentBuilderTest.java │ │ └── runner │ │ │ ├── AppLauncherTest.java │ │ │ └── ui │ │ │ └── AbstractSuitePresenterTest.java │ │ └── util │ │ ├── io │ │ ├── directories │ │ │ └── UnitTestDirectories.java │ │ └── DirectoryManagerTest.java │ │ ├── SimpleEntry.java │ │ ├── StringsTest.java │ │ └── KoanComparatorTest.java ├── build.gradle └── gradlew.bat ├── .gitignore ├── .travis.yml ├── docker ├── Dockerfile └── spec │ └── Dockerfile_spec.rb └── README.md /koans/app/lib/koans.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NansD/java-koans/NansDumortier/koans/app/lib/koans.jar -------------------------------------------------------------------------------- /lib/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NansD/java-koans/NansDumortier/lib/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /koans/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NansD/java-koans/NansDumortier/koans/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/Builder.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util; 2 | 3 | public interface Builder { 4 | 5 | T build(); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /koans/app/config/compilationcommands.properties: -------------------------------------------------------------------------------- 1 | .groovy=groovyc -d ${bindir} -classpath ${classpath} ${filename} 2 | .java=javac -d ${bindir} -classpath ${classpath} ${filename} 3 | 4 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/ui/ErrorPresenter.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io.ui; 2 | 3 | public interface ErrorPresenter { 4 | 5 | void displayError(String error); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /koans/app/config/config.properties: -------------------------------------------------------------------------------- 1 | compile_timeout_in_ms=5000 2 | ignore_from_monitoring=.*\\.idea 3 | debug=false 4 | exit_character=Q 5 | enable_encouragement=false 6 | path_xml_filename=PathToEnlightenment.xml 7 | enable_expectation_result=true 8 | interactive=true 9 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/FileListener.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | 5 | public interface FileListener { 6 | 7 | void fileSaved(File file); 8 | void newFile(File file); 9 | void fileDeleted(File file); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/ui/DefaultErrorPresenter.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io.ui; 2 | 3 | public class DefaultErrorPresenter implements ErrorPresenter { 4 | 5 | public void displayError(String error) { 6 | System.err.println(error); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/suite/OnePassingKoanDifferentName.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.suite; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | public class OnePassingKoanDifferentName extends OnePassingKoan { 6 | 7 | @Koan 8 | public void koan() { } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | */bin/* 3 | .metadata 4 | bin/* 5 | lib/build 6 | lib/out 7 | koans/data 8 | koans/data* 9 | .classpath 10 | .project 11 | .settings 12 | koans/app/data 13 | lib/file-compiler/bin 14 | **/file_hashes.dat 15 | .gradle 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea 20 | -------------------------------------------------------------------------------- /koans/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Mar 26 18:10:52 GMT 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-bin.zip 7 | -------------------------------------------------------------------------------- /lib/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Mar 26 18:10:52 GMT 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-bin.zip 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: ruby 4 | 5 | services: 6 | - docker 7 | 8 | before_install: 9 | - docker build -t java/koans docker 10 | 11 | script: 12 | - docker run java/koans /bin/bash -c "git clone https://github.com/matyb/java-koans && gradle -p java-koans/lib buildApp" 13 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/behavior/ArgumentBehavior.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline.behavior; 2 | 3 | 4 | public interface ArgumentBehavior { 5 | 6 | void run(String... values) throws Exception; 7 | String getErrorMessage(); 8 | String getSuccessMessage(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/path/xmltransformation/XmlToPathTransformer.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.path.xmltransformation; 2 | 3 | import com.sandwich.koan.path.PathToEnlightenment.Path; 4 | 5 | public interface XmlToPathTransformer { 6 | 7 | public Path transform(); 8 | 9 | } 10 | 11 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/suite/OneFailingKoan.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.suite; 2 | 3 | import static com.sandwich.util.Assert.assertEquals; 4 | 5 | import com.sandwich.koan.Koan; 6 | 7 | public class OneFailingKoan { 8 | @Koan 9 | public void koanMethod() { 10 | assertEquals(true, false); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/directories/ProductionDirectories.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io.directories; 2 | 3 | 4 | public class ProductionDirectories extends DirectorySet { 5 | 6 | public String getProjectDir() { 7 | return "koans"; 8 | } 9 | 10 | public String getSourceDir() { 11 | return "src"; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /koans/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'idea' 3 | apply plugin: 'eclipse' 4 | 5 | repositories { 6 | flatDir { 7 | dirs 'app/lib' 8 | } 9 | } 10 | 11 | sourceSets { 12 | main { 13 | java { 14 | srcDir 'src' 15 | } 16 | } 17 | } 18 | 19 | dependencies { 20 | compile name: 'koans' 21 | } -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/suite/OnePassingKoan.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.suite; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | public class OnePassingKoan { 6 | boolean[] invoked; 7 | public OnePassingKoan(){ 8 | invoked = new boolean[]{false}; 9 | } 10 | @Koan 11 | public void koan() { 12 | invoked[0] = true; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/Koan.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.METHOD) 10 | public @interface Koan {} 11 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/suite/WrongExpectationOrderKoan.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.suite; 2 | 3 | import static com.sandwich.koan.constant.KoanConstants.__; 4 | 5 | import com.sandwich.koan.Koan; 6 | 7 | public class WrongExpectationOrderKoan { 8 | @Koan 9 | public void expectationOnLeft() { 10 | com.sandwich.util.Assert.assertEquals(__, false); 11 | } 12 | } -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/suite/OneFailingKoanDifferentName.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.suite; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import com.sandwich.koan.Koan; 6 | 7 | public class OneFailingKoanDifferentName extends OneFailingKoan { 8 | @Koan 9 | @Override 10 | public void koanMethod() { 11 | assertEquals(true, false); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/filecompiler/CompilationListener.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io.filecompiler; 2 | 3 | import java.io.File; 4 | 5 | public interface CompilationListener { 6 | void compilationFailed(File src, String[] command, int exitCode, String errorMessage, Throwable x); 7 | void compilationSucceeded(File src, String[] command, String stdIo, Throwable x); 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/suite/BlowUpOnLineTen.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.suite; 2 | 3 | import com.sandwich.koan.Koan; 4 | import com.sandwich.koan.KoanIncompleteException; 5 | 6 | public class BlowUpOnLineTen { 7 | // gotta blow up on line 10 thus the comment 8 | @Koan 9 | public void blowUpOnLineTen(){ 10 | throw new KoanIncompleteException(null); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/FileAction.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | public interface FileAction { 7 | 8 | void onFile(File file) throws IOException; 9 | void onDirectory(File dir) throws IOException; 10 | void onNull(File nullFile) throws IOException; 11 | void onNew(File newFile) throws IOException; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/suite/TwoFailingKoans.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.suite; 2 | 3 | import static com.sandwich.util.Assert.assertEquals; 4 | 5 | import com.sandwich.koan.Koan; 6 | 7 | public class TwoFailingKoans { 8 | @Koan 9 | public void koanTwo() { 10 | assertEquals(true, false); 11 | } 12 | 13 | @Koan 14 | public void koanMethod() { 15 | assertEquals(true, false); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/suite/BlowUpOnLineEleven.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.suite; 2 | 3 | import com.sandwich.koan.Koan; 4 | import com.sandwich.koan.KoanIncompleteException; 5 | 6 | public class BlowUpOnLineEleven { 7 | // gotta put it on line 11 thus the two lines here 8 | // 9 | @Koan 10 | public void blowUpOnLineEleven() { 11 | throw new KoanIncompleteException(null); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/KoanIncompleteException.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan; 2 | 3 | public class KoanIncompleteException extends AssertionError { 4 | 5 | private static final long serialVersionUID = -4207677767865451508L; 6 | 7 | public KoanIncompleteException(String message){ 8 | super(message); 9 | } 10 | 11 | @Override public String toString(){ 12 | return super.getMessage(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/ui/SuitePresenter.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.ui; 2 | 3 | import com.sandwich.koan.result.KoanSuiteResult; 4 | import com.sandwich.util.io.ui.ErrorPresenter; 5 | 6 | public interface SuitePresenter extends ErrorPresenter { 7 | 8 | public void displayResult(KoanSuiteResult result); 9 | public void displayError(String error); 10 | public void displayMessage(String error); 11 | public void clearMessages(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/behavior/Debug.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline.behavior; 2 | 3 | import com.sandwich.koan.ApplicationSettings; 4 | 5 | public class Debug extends AbstractArgumentBehavior{ 6 | 7 | public void run(String... args){ 8 | ApplicationSettings.setDebug(ApplicationSettings.isDebug() || 9 | "true".equalsIgnoreCase(args[0]) || 10 | args[0] == null || 11 | args[0].trim().length() == 0); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/util/io/directories/UnitTestDirectories.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io.directories; 2 | 3 | 4 | 5 | public class UnitTestDirectories extends ProductionDirectories { 6 | 7 | public String getSourceDir() { 8 | return "java"; 9 | } 10 | 11 | public String getProjectDir() { 12 | String sep = System.getProperty("file.separator"); 13 | return super.getLibrariesDir() + sep 14 | + "src" + sep + "test"; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/behavior/Reset.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline.behavior; 2 | 3 | import java.io.IOException; 4 | 5 | import com.sandwich.util.io.CopyFileOperation; 6 | 7 | public class Reset extends KoanFileCopying { 8 | 9 | @Override 10 | protected void copy(String backupSrcDirectory, String appSrcDirectory) 11 | throws IOException { 12 | new CopyFileOperation(backupSrcDirectory, appSrcDirectory).operate(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/StreamUtils.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.InputStream; 4 | import java.util.NoSuchElementException; 5 | import java.util.Scanner; 6 | 7 | public class StreamUtils { 8 | 9 | public static String convertStreamToString(InputStream stream) { 10 | try { 11 | return new Scanner(stream).useDelimiter("\\A").next(); 12 | } catch (NoSuchElementException e) { 13 | return ""; 14 | } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/behavior/AbstractArgumentBehavior.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline.behavior; 2 | 3 | import com.sandwich.util.Strings; 4 | 5 | public abstract class AbstractArgumentBehavior implements ArgumentBehavior{ 6 | 7 | public String getErrorMessage() { 8 | return Strings.getMessage(getClass(), "error"); 9 | } 10 | 11 | public String getSuccessMessage() { 12 | return Strings.getMessage(getClass(), "success"); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/ExceptionUtils.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | 6 | public class ExceptionUtils { 7 | 8 | public static String convertToPopulatedStackTraceString(Throwable t) { 9 | StringWriter stringWriter = new StringWriter(); 10 | if(t == null){ 11 | return ""; 12 | } 13 | t.printStackTrace(new PrintWriter(stringWriter)); 14 | return stringWriter.toString(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/ForEachFileAction.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | public abstract class ForEachFileAction extends ExistingFileAction { 7 | 8 | public ForEachFileAction(String... strings) { 9 | super(strings); 10 | } 11 | 12 | public void onDirectory(File dir) throws IOException { 13 | for (String fileName : dir.list()) { 14 | operate(new File(dir, fileName)); 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/behavior/MethodArg.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline.behavior; 2 | 3 | import com.sandwich.koan.path.PathToEnlightenment; 4 | 5 | public class MethodArg extends AbstractArgumentBehavior { 6 | 7 | public void run(String...koanName) { 8 | if(koanName != null && 9 | koanName.length > 0 && 10 | koanName[0] != null && 11 | koanName[0].trim().length() != 0){ 12 | PathToEnlightenment.filterByKoan(koanName[0]); 13 | } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutKoans.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.util.Assert.fail; 6 | 7 | public class AboutKoans { 8 | 9 | @Koan 10 | public void findAboutKoansFile() { 11 | } 12 | 13 | @Koan 14 | public void definitionOfKoanCompletion() { 15 | boolean koanIsComplete = true; 16 | if (!koanIsComplete) { 17 | fail("what if koanIsComplete variable was true?"); 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutKoans.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.util.Assert.fail; 6 | 7 | public class AboutKoans { 8 | 9 | @Koan 10 | public void findAboutKoansFile() { 11 | fail("delete this line to advance"); 12 | } 13 | 14 | @Koan 15 | public void definitionOfKoanCompletion() { 16 | boolean koanIsComplete = false; 17 | if (!koanIsComplete) { 18 | fail("what if koanIsComplete variable was true?"); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/behavior/ClassArg.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline.behavior; 2 | 3 | import com.sandwich.koan.path.PathToEnlightenment; 4 | 5 | public class ClassArg extends AbstractArgumentBehavior { 6 | 7 | public void run(String...koanSuiteClassName) { 8 | if(koanSuiteClassName != null && 9 | koanSuiteClassName.length > 0 && 10 | koanSuiteClassName[0] != null && 11 | koanSuiteClassName[0].trim().length() != 0){ 12 | PathToEnlightenment.filterBySuite(koanSuiteClassName[0]); 13 | } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/path/xmltransformation/XmlVariableDictionary.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.path.xmltransformation; 2 | 3 | import com.sandwich.koan.constant.KoanConstants; 4 | 5 | public class XmlVariableDictionary { 6 | 7 | public static final String METHOD_NAME = wrapParam("method_name"); 8 | public static final String FILE_NAME = wrapParam("file_name"); 9 | public static final String FILE_PATH = wrapParam("file_path"); 10 | 11 | private static String wrapParam(String param) { 12 | return new StringBuilder(KoanConstants.XML_PARAMETER_START) 13 | .append(param).append(KoanConstants.XML_PARAMETER_END).toString(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /koans/README.md: -------------------------------------------------------------------------------- 1 | ## Quick Start 2 | * Download and unarchive the contents of the most recent java-koans in development from: 3 | https://github.com/matyb/java-koans/archive/master.zip 4 | * Open a console (terminal) and cd to the directory you unarchived: 5 | ```cd ``` 6 | * Change directory to the koans directory: ```cd koans``` 7 | * To export koans to run below command and open in IDE: 8 | * IDEA IntelliJ: ```./gradlew idea (WINDOWS: gradlew.bat idea)``` 9 | * Eclipse ```./gradlew eclipse (WINDOWS: gradlew.bat eclipse)``` 10 | * Back to the console and run ```./run.sh``` (WINDOWS: ```run.bat```) 11 | * Follow the instructions and start hacking 12 | -------------------------------------------------------------------------------- /koans/src/java8/AboutLocalTime.java: -------------------------------------------------------------------------------- 1 | package java8; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.time.LocalTime; 6 | import java.time.temporal.ChronoUnit; 7 | 8 | import static com.sandwich.koan.constant.KoanConstants.__; 9 | import static com.sandwich.util.Assert.assertEquals; 10 | 11 | public class AboutLocalTime { 12 | 13 | @Koan 14 | public void localTime() { 15 | LocalTime t1 = LocalTime.of(7, 30); 16 | assertEquals(t1, LocalTime.parse(__)); 17 | } 18 | 19 | @Koan 20 | public void localTimeMinus() { 21 | LocalTime t1 = LocalTime.parse("10:30"); 22 | LocalTime t2 = t1.minus(2, ChronoUnit.HOURS); 23 | assertEquals(t2, LocalTime.parse(__)); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/ExistingFileAction.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | public abstract class ExistingFileAction extends FileOperation { 7 | 8 | public ExistingFileAction(String... strings) { 9 | super(strings); 10 | } 11 | 12 | public void onNull(File file) throws IOException { 13 | throwNonExistentFileError(String.valueOf(file)); 14 | } 15 | 16 | public void onNew(File file) throws IOException { 17 | throwNonExistentFileError(file.getAbsolutePath()); 18 | } 19 | 20 | private String throwNonExistentFileError(String path) { 21 | throw new IllegalArgumentException("Source path must actually exist. ("+ path +")"); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /koans/app/data/src/java8/AboutLocalTime.java: -------------------------------------------------------------------------------- 1 | package java8; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.time.LocalTime; 6 | import java.time.temporal.ChronoUnit; 7 | 8 | import static com.sandwich.koan.constant.KoanConstants.__; 9 | import static com.sandwich.util.Assert.assertEquals; 10 | 11 | public class AboutLocalTime { 12 | 13 | @Koan 14 | public void localTime() { 15 | LocalTime t1 = LocalTime.of(7, 30); 16 | assertEquals(t1, LocalTime.parse(__)); 17 | } 18 | 19 | @Koan 20 | public void localTimeMinus() { 21 | LocalTime t1 = LocalTime.parse("10:30"); 22 | LocalTime t2 = t1.minus(2, ChronoUnit.HOURS); 23 | assertEquals(t2, LocalTime.parse(__)); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | MAINTAINER Mat Bentley 3 | 4 | ENV JAVA_VERSION 1.8.0 5 | ENV GRADLE_VERSION 3.4.1 6 | 7 | # install wget, git, curl, jdk, which 8 | RUN yum remove -y java &&\ 9 | yum install -y wget git curl unzip java-$JAVA_VERSION-openjdk-devel which 10 | 11 | # install gradle 12 | RUN wget https://services.gradle.org/distributions/gradle-$GRADLE_VERSION-bin.zip &&\ 13 | mkdir -p /etc/alternatives/gradle &&\ 14 | unzip -d /etc/alternatives/gradle gradle-$GRADLE_VERSION-bin.zip &&\ 15 | ln -s /etc/alternatives/gradle/gradle-$GRADLE_VERSION /opt/gradle 16 | 17 | ENV PATH $PATH:/opt/gradle/bin 18 | RUN JAVA_HOME=$(readlink $(readlink `which java`) | gawk '$0=gensub(/\/jre\/bin\/java/,"",1)') 19 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/behavior/Backup.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline.behavior; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import com.sandwich.util.io.CopyFileOperation; 7 | 8 | public class Backup extends KoanFileCopying{ 9 | 10 | @Override 11 | protected void copy(String backupSrcDirectory, String appSrcDirectory) 12 | throws IOException { 13 | File backupDir = new File(backupSrcDirectory); 14 | if(!backupDir.exists()){ 15 | backupDir.mkdirs(); 16 | } 17 | File sourceDir = new File(appSrcDirectory); 18 | new CopyFileOperation(sourceDir, backupDir){ 19 | public void onNew(File file) throws IOException { 20 | file.mkdirs(); 21 | }; 22 | }.operate(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | 8 | public class FileUtils { 9 | 10 | public static String readFileAsString(File file) { 11 | byte[] buffer = new byte[(int) file.length()]; 12 | BufferedInputStream f = null; 13 | try { 14 | f = new BufferedInputStream(new FileInputStream(file)); 15 | f.read(buffer); 16 | } catch (IOException e) { 17 | throw new RuntimeException(e); 18 | } finally { 19 | if (f != null) try { f.close(); } catch (IOException ignored) { } 20 | } 21 | return new String(buffer); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /koans/run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cls 3 | setLocal EnableDelayedExpansion 4 | set string=%~dp0 5 | set string=%string:\=/% 6 | set CLASSPATH="%string%app/bin";"%string%app/config" 7 | for /R "%~dp0/app/lib" %%a in (*.jar) do ( 8 | set string=%%a 9 | set string=!string:\=/! 10 | set CLASSPATH=!CLASSPATH!;"!string!" 11 | ) 12 | set CLASSPATH=!CLASSPATH!; 13 | javac -version 14 | if ERRORLEVEL 3 goto no_javac 15 | java -version 16 | if ERRORLEVEL 1 goto no_java 17 | cls 18 | java -Dapplication.basedir="%~dp0"" -classpath %CLASSPATH% com.sandwich.koan.runner.AppLauncher %1 %2 %3 %4 %5 %6 %7 %8 %9 19 | 20 | goto end 21 | 22 | :no_java 23 | cls 24 | @echo java is not bound to PATH variable. 25 | goto end 26 | 27 | :no_javac 28 | cls 29 | @echo javac is not bound to PATH variable. 30 | goto end 31 | 32 | :end -------------------------------------------------------------------------------- /koans/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR="$( cd -P "$( dirname "$0" )" && pwd )" 3 | exitOnError() 4 | { 5 | rc=$? 6 | if [[ $rc != 0 ]] ; then 7 | echo ${1}' is missing from your PATH.' 8 | exit $rc 9 | fi 10 | } 11 | 12 | buildClasspath() 13 | { 14 | appDir=$1 15 | classpath=$appDir/bin 16 | IFS=$'\n' 17 | classpath=$classpath:$appDir/config/ 18 | for jar in $appDir/lib/* 19 | do 20 | classpath=$classpath:$jar 21 | done 22 | } 23 | javac -help > /dev/null 2>&1 24 | exitOnError 'javac' 25 | java -version > /dev/null 2>&1 26 | exitOnError 'java' 27 | buildClasspath "$DIR"/app 28 | cmd="java -Dapplication.basedir=\"$DIR\" -classpath \"$classpath\" com.sandwich.koan.runner.AppLauncher "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"" 29 | eval $cmd 30 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/AnonymousInnerClassTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.junit.Test; 6 | 7 | import com.sandwich.koan.cmdline.CommandLineArgumentRunner; 8 | import com.sandwich.koan.path.CommandLineTestCase; 9 | 10 | public class AnonymousInnerClassTest extends CommandLineTestCase{ 11 | 12 | @Test 13 | public void testAnonymousInnerClassIsCoolToUseAsKoan() throws Exception { 14 | final String definitelyAUniqueString = "meh1294120240912049"; 15 | stubAllKoans(Arrays.asList(new Object(){ 16 | @Koan public void printMsg(){ 17 | System.out.println(definitelyAUniqueString); 18 | } 19 | })); 20 | new CommandLineArgumentRunner().run(); 21 | assertSystemOutContains(definitelyAUniqueString); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /docker/spec/Dockerfile_spec.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/ruby 2 | 3 | require "serverspec" 4 | require "docker" 5 | 6 | describe "Dockerfile" do 7 | before(:all) do 8 | image = Docker::Image.build_from_dir('.') 9 | 10 | set :os, family: :redhat 11 | set :backend, :docker 12 | set :docker_image, image.id 13 | end 14 | 15 | it "has jdk8 installed" do 16 | expect(command("javac -version").stderr).to include(" 1.8.") 17 | end 18 | 19 | it "has gradle installed" do 20 | expect(command("gradle -version").stdout).to include("Build time") 21 | end 22 | 23 | it "can build app" do 24 | output = command("git clone https://github.com/matyb/java-koans && gradle -p java-koans/lib buildApp").stdout 25 | expect(output).to include(":test\n") 26 | expect(output).to include("BUILD SUCCESSFUL") 27 | end 28 | 29 | end 30 | 31 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/ui/AbstractSuitePresenter.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.ui; 2 | 3 | import com.sandwich.koan.result.KoanSuiteResult; 4 | 5 | public abstract class AbstractSuitePresenter implements SuitePresenter { 6 | 7 | public void displayResult(KoanSuiteResult result) { 8 | if (result.isAllKoansSuccessful()) { 9 | displayAllSuccess(result); 10 | } else { 11 | displayOneOrMoreFailure(result); 12 | } 13 | displayChart(result); 14 | displayPassingFailing(result); 15 | displayHeader(result); 16 | } 17 | 18 | abstract protected void displayHeader(KoanSuiteResult result); 19 | abstract protected void displayPassingFailing(KoanSuiteResult result); 20 | abstract protected void displayChart(KoanSuiteResult result); 21 | abstract protected void displayOneOrMoreFailure(KoanSuiteResult result); 22 | abstract protected void displayAllSuccess(KoanSuiteResult result); 23 | } -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/behavior/KoanFileCopying.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline.behavior; 2 | 3 | import java.io.IOException; 4 | 5 | import com.sandwich.koan.ui.SuitePresenter; 6 | import com.sandwich.koan.util.ApplicationUtils; 7 | import com.sandwich.util.io.directories.DirectoryManager; 8 | 9 | public abstract class KoanFileCopying extends AbstractArgumentBehavior { 10 | 11 | public void run(String... values) { 12 | SuitePresenter presenter = ApplicationUtils.getPresenter(); 13 | try { 14 | copy(DirectoryManager.getProjectDataSourceDir(), DirectoryManager.getSourceDir()); 15 | } catch (IOException e) { 16 | e.printStackTrace(); 17 | presenter.displayError(getErrorMessage()); 18 | System.exit(-1); 19 | } 20 | presenter.displayMessage(getSuccessMessage()); 21 | } 22 | 23 | protected abstract void copy(String backupSrcDirectory, String appSrcDirectory) throws IOException; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /koans/src/java7/AboutStringsInSwitch.java: -------------------------------------------------------------------------------- 1 | package java7; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutStringsInSwitch { 9 | 10 | @Koan 11 | public void stringsInSwitchStatement() { 12 | String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; 13 | String dangerous = null; 14 | String notDangerous = null; 15 | for (String animal : animals) { 16 | switch (animal) { 17 | case "Tiger": 18 | dangerous = animal; 19 | case "Dog": 20 | case "Cat": 21 | case "Elephant": 22 | case "Zebra": 23 | notDangerous = animal; 24 | } 25 | } 26 | assertEquals(notDangerous, __); 27 | assertEquals(dangerous, __); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /koans/app/data/src/java7/AboutStringsInSwitch.java: -------------------------------------------------------------------------------- 1 | package java7; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutStringsInSwitch { 9 | 10 | @Koan 11 | public void stringsInSwitchStatement() { 12 | String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; 13 | String dangerous = null; 14 | String notDangerous = null; 15 | for (String animal : animals) { 16 | switch (animal) { 17 | case "Tiger": 18 | dangerous = animal; 19 | case "Dog": 20 | case "Cat": 21 | case "Elephant": 22 | case "Zebra": 23 | notDangerous = animal; 24 | } 25 | } 26 | assertEquals(notDangerous, __); 27 | assertEquals(dangerous, __); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /koans/src/java8/AboutMultipleInheritance.java: -------------------------------------------------------------------------------- 1 | package java8; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutMultipleInheritance { 9 | 10 | interface Human { 11 | default String sound() { 12 | return "hello"; 13 | } 14 | } 15 | 16 | interface Bull { 17 | default String sound() { 18 | return "moo"; 19 | } 20 | } 21 | 22 | class Minotaur implements Human, Bull { 23 | //both interfaces implement same default method 24 | //has to be overridden 25 | @Override 26 | public String sound() { 27 | return Bull.super.sound(); 28 | } 29 | } 30 | 31 | @Koan 32 | public void multipleInheritance() { 33 | Minotaur minotaur = new Minotaur(); 34 | assertEquals(minotaur.sound(), __); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /koans/app/data/src/java8/AboutMultipleInheritance.java: -------------------------------------------------------------------------------- 1 | package java8; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutMultipleInheritance { 9 | 10 | interface Human { 11 | default String sound() { 12 | return "hello"; 13 | } 14 | } 15 | 16 | interface Bull { 17 | default String sound() { 18 | return "moo"; 19 | } 20 | } 21 | 22 | class Minotaur implements Human, Bull { 23 | //both interfaces implement same default method 24 | //has to be overridden 25 | @Override 26 | public String sound() { 27 | return Bull.super.sound(); 28 | } 29 | } 30 | 31 | @Koan 32 | public void multipleInheritance() { 33 | Minotaur minotaur = new Minotaur(); 34 | assertEquals(minotaur.sound(), __); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/util/ApplicationUtils.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.util; 2 | 3 | import java.io.File; 4 | 5 | import com.sandwich.koan.ui.ConsolePresenter; 6 | import com.sandwich.koan.ui.SuitePresenter; 7 | import com.sandwich.util.io.directories.DirectoryManager; 8 | 9 | public class ApplicationUtils { 10 | 11 | private static SuitePresenterFactory suitePresenterFactory = new SuitePresenterFactory(); 12 | 13 | static public boolean isFirstTimeAppHasBeenRun() { 14 | File dataDirectory = new File(DirectoryManager.getDataDir()); 15 | return !dataDirectory.exists(); 16 | } 17 | 18 | static public boolean isWindows(){ 19 | return System.getProperty("os.name").toLowerCase().contains("win"); 20 | } 21 | 22 | static public SuitePresenter getPresenter(){ 23 | return suitePresenterFactory.create(); 24 | } 25 | 26 | public static class SuitePresenterFactory { 27 | protected SuitePresenter create(){ 28 | return new ConsolePresenter(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgumentRunner.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import com.sandwich.koan.constant.ArgumentType; 9 | 10 | public class CommandLineArgumentRunner implements Runnable { 11 | 12 | private final Map commandLineArguments; 13 | 14 | public CommandLineArgumentRunner(){ 15 | this(new CommandLineArgumentBuilder()); 16 | } 17 | 18 | public CommandLineArgumentRunner(Map commandLineArguments) { 19 | this.commandLineArguments = Collections.unmodifiableMap(commandLineArguments); 20 | } 21 | 22 | public void run() { 23 | List sortedArguments = new ArrayList(commandLineArguments.values()); 24 | Collections.sort(sortedArguments); 25 | for(CommandLineArgument argument : sortedArguments){ 26 | argument.run(); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/CopyFileOperation.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | 10 | public class CopyFileOperation extends SourceAndDestinationFileAction { 11 | 12 | public CopyFileOperation(File source, File destination) { 13 | super(source, destination); 14 | } 15 | 16 | public CopyFileOperation(String sourceDir, String destinationDir) { 17 | super(sourceDir, destinationDir); 18 | } 19 | 20 | public void sourceToDestination(File src, File dest) throws IOException { 21 | InputStream in = new FileInputStream(src); 22 | if(!dest.getParentFile().exists()){ 23 | dest.getParentFile().mkdirs(); 24 | } 25 | OutputStream out = new FileOutputStream(dest); 26 | 27 | // Transfer bytes from in to out 28 | byte[] buf = new byte[1024]; 29 | int len; 30 | 31 | while ((len = in.read(buf)) > 0) { 32 | out.write(buf, 0, len); 33 | } 34 | 35 | in.close(); 36 | out.close(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /koans/src/java8/AboutOptional.java: -------------------------------------------------------------------------------- 1 | package java8; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.Optional; 6 | 7 | import static com.sandwich.koan.constant.KoanConstants.__; 8 | import static com.sandwich.util.Assert.assertEquals; 9 | 10 | public class AboutOptional { 11 | 12 | boolean optionalIsPresentField = false; 13 | 14 | @Koan 15 | public void isPresent() { 16 | boolean optionalIsPresent = false; 17 | Optional value = notPresent(); 18 | if (value.isPresent()) { 19 | optionalIsPresent = true; 20 | } 21 | assertEquals(optionalIsPresent, __); 22 | } 23 | 24 | @Koan 25 | public void ifPresentLambda() { 26 | Optional value = notPresent(); 27 | value.ifPresent(x -> optionalIsPresentField = true); 28 | assertEquals(optionalIsPresentField, __); 29 | } 30 | 31 | //use optional on api to signal that value is optional 32 | public Optional notPresent() { 33 | return Optional.empty(); 34 | } 35 | 36 | private Optional present() { 37 | return Optional.of("is present"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /koans/app/data/src/java8/AboutOptional.java: -------------------------------------------------------------------------------- 1 | package java8; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.Optional; 6 | 7 | import static com.sandwich.koan.constant.KoanConstants.__; 8 | import static com.sandwich.util.Assert.assertEquals; 9 | 10 | public class AboutOptional { 11 | 12 | boolean optionalIsPresentField = false; 13 | 14 | @Koan 15 | public void isPresent() { 16 | boolean optionalIsPresent = false; 17 | Optional value = notPresent(); 18 | if (value.isPresent()) { 19 | optionalIsPresent = true; 20 | } 21 | assertEquals(optionalIsPresent, __); 22 | } 23 | 24 | @Koan 25 | public void ifPresentLambda() { 26 | Optional value = notPresent(); 27 | value.ifPresent(x -> optionalIsPresentField = true); 28 | assertEquals(optionalIsPresentField, __); 29 | } 30 | 31 | //use optional on api to signal that value is optional 32 | public Optional notPresent() { 33 | return Optional.empty(); 34 | } 35 | 36 | private Optional present() { 37 | return Optional.of("is present"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'idea' 3 | apply plugin: 'eclipse' 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | testCompile 'junit:junit:4.+' 11 | testCompile 'org.easymock:easymock:3.4' 12 | } 13 | 14 | task createJar(type: Jar){ 15 | archiveName = 'koans.jar' 16 | manifest { 17 | attributes 'Implementation-Title': 'Java Koans', 18 | 'Implementation-Version': 2.0, 19 | 'Main-Class': 'com.sandwich.koan.runner.AppLauncher' 20 | } 21 | baseName = "java-koans" 22 | from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }} 23 | with jar 24 | } 25 | 26 | task copyJarToBin { 27 | doLast { 28 | copy { 29 | from './build/libs/koans.jar' 30 | into '../koans/app/lib' 31 | } 32 | } 33 | } 34 | 35 | test { 36 | 37 | classpath = project.sourceSets.test.runtimeClasspath + files("${projectDir}/../koans/app/config") 38 | 39 | } 40 | 41 | allprojects { 42 | sourceCompatibility = 1.5 43 | targetCompatibility = 1.5 44 | } 45 | 46 | task buildApp (dependsOn: [clean, test, createJar, copyJarToBin]) 47 | 48 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/KoanSuiteCompilationListener.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | 5 | import com.sandwich.util.io.filecompiler.CompilationListener; 6 | import com.sandwich.util.io.filecompiler.FileCompilerAction; 7 | 8 | public class KoanSuiteCompilationListener implements CompilationListener { 9 | 10 | private boolean lastCompilationAttemptFailed = false; 11 | private String lastMessageShown = null; 12 | 13 | public void compilationFailed(File src, String[] command, int exitCode, String errorMessage, Throwable x) { 14 | if(lastMessageShown == null || !errorMessage.trim().equals(lastMessageShown.trim())){ 15 | FileCompilerAction.LOGGING_HANDLER.compilationFailed(src, command, exitCode, errorMessage, x); 16 | } 17 | lastMessageShown = errorMessage; 18 | lastCompilationAttemptFailed = true; 19 | } 20 | 21 | public void compilationSucceeded(File src, String[] command, String stdIo, Throwable x) { 22 | lastMessageShown = null; // reset last failed compilation message 23 | lastCompilationAttemptFailed = false; 24 | } 25 | 26 | public boolean isLastCompilationAttemptFailure(){ 27 | return lastCompilationAttemptFailed; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutConstructors.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | 4 | import com.sandwich.koan.Koan; 5 | 6 | import static com.sandwich.koan.constant.KoanConstants.__; 7 | import static com.sandwich.util.Assert.assertEquals; 8 | 9 | public class AboutConstructors { 10 | 11 | class A { 12 | String someString = "a"; 13 | 14 | public A() { 15 | someString += "x"; 16 | } 17 | 18 | } 19 | 20 | class B extends A { 21 | public B() { 22 | someString += "g"; 23 | } 24 | 25 | } 26 | 27 | @Koan 28 | public void simpleConstructorOrder() { 29 | assertEquals(new B().someString, __); 30 | } 31 | 32 | class Aa { 33 | String someString = "a"; 34 | 35 | public Aa() { 36 | someString += "x"; 37 | } 38 | 39 | public Aa(String s) { 40 | someString += s; 41 | } 42 | } 43 | 44 | class Bb extends Aa { 45 | public Bb() { 46 | super("Boo"); 47 | someString += "g"; 48 | } 49 | 50 | } 51 | 52 | @Koan 53 | public void complexConstructorOrder() { 54 | assertEquals(new Bb().someString, __); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/result/KoanMethodResult.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.result; 2 | 3 | import com.sandwich.koan.KoanMethod; 4 | 5 | public class KoanMethodResult { 6 | 7 | public final static KoanMethodResult PASSED = new KoanMethodResult(null,"PASSED","PASSED"); 8 | 9 | private final String message; 10 | private final String lineNumber; 11 | private final KoanMethod failingMethod; 12 | 13 | public KoanMethodResult(KoanMethod failingMethod, String message, String lineNumber){ 14 | this.message = message; 15 | this.failingMethod = failingMethod; 16 | this.lineNumber = lineNumber; 17 | } 18 | 19 | public static KoanMethodResult getPassing() { 20 | return PASSED; 21 | } 22 | 23 | public String getMessage() { 24 | return message; 25 | } 26 | 27 | public String getLineNumber() { 28 | return lineNumber; 29 | } 30 | 31 | public KoanMethod getFailingMethod() { 32 | return failingMethod; 33 | } 34 | 35 | public boolean isPassed(){ 36 | return this == PASSED; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "KoanMethodResult [message=" + message + ", lineNumber=" 42 | + lineNumber + ", failingMethod=" + failingMethod + "]"; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutConstructors.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | 4 | import com.sandwich.koan.Koan; 5 | 6 | import static com.sandwich.koan.constant.KoanConstants.__; 7 | import static com.sandwich.util.Assert.assertEquals; 8 | 9 | public class AboutConstructors { 10 | 11 | class A { 12 | String someString = "a"; 13 | 14 | public A() { 15 | someString += "x"; 16 | } 17 | 18 | } 19 | 20 | class B extends A { 21 | public B() { 22 | someString += "g"; 23 | } 24 | 25 | } 26 | 27 | @Koan 28 | public void simpleConstructorOrder() { 29 | assertEquals(new B().someString, __); 30 | } 31 | 32 | class Aa { 33 | String someString = "a"; 34 | 35 | public Aa() { 36 | someString += "x"; 37 | } 38 | 39 | public Aa(String s) { 40 | someString += s; 41 | } 42 | } 43 | 44 | class Bb extends Aa { 45 | public Bb() { 46 | super("Boo"); 47 | someString += "g"; 48 | } 49 | 50 | } 51 | 52 | @Koan 53 | public void complexConstructorOrder() { 54 | assertEquals(new Bb().someString, __); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/constant/KoanConstants.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.constant; 2 | 3 | import com.sandwich.util.Strings; 4 | 5 | public abstract class KoanConstants { 6 | 7 | private KoanConstants(){} 8 | 9 | public static final String __ = Strings.getMessage("__"); 10 | 11 | public static final String EOL = System.getProperty("line.separator"); 12 | public static final String EOLS = "[\n\r"+EOL+"]"; 13 | 14 | public static final String PERIOD = "."; 15 | public static final String EXPECTATION_LEFT_ARG= "has expectation as wrong argument!"; 16 | public static final String EXPECTED_LEFT = ":<"; 17 | public static final String EXPECTED_RIGHT = ">"; 18 | public static final String LINE_NO_START = ".java:"; 19 | public static final String LINE_NO_END = ")"; 20 | 21 | public static final String COMPLETE_CHAR = "X"; 22 | public static final String INCOMPLETE_CHAR = "-"; 23 | 24 | public static final int PROGRESS_BAR_WIDTH = 50; 25 | public static final String PROGRESS_BAR_START = "["; 26 | public static final String PROGRESS_BAR_END = "]"; 27 | 28 | public static final String XML_PARAMETER_START = "${"; 29 | public static final String XML_PARAMETER_END = "}"; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /koans/src/java7/AboutDiamondOperator.java: -------------------------------------------------------------------------------- 1 | package java7; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | import static com.sandwich.koan.constant.KoanConstants.__; 10 | import static com.sandwich.util.Assert.assertEquals; 11 | 12 | public class AboutDiamondOperator { 13 | 14 | @Koan 15 | public void diamondOperator() { 16 | String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; 17 | //Generic type of array list inferred - empty <> operator 18 | List animalsList = new ArrayList<>(Arrays.asList(animals)); 19 | assertEquals(animalsList, __); 20 | } 21 | 22 | @Koan 23 | public void diamondOperatorInMethodCall() { 24 | String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; 25 | //type of new ArrayList<>() inferred from method parameter 26 | List animalsList = fill(new ArrayList<>()); 27 | assertEquals(animalsList, __); 28 | } 29 | 30 | private List fill(List list) { 31 | String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; 32 | list.addAll(Arrays.asList(animals)); 33 | return list; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/constant/ArgumentTypeTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.constant; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | import org.junit.Test; 11 | 12 | import com.sandwich.koan.path.CommandLineTestCase; 13 | 14 | public class ArgumentTypeTest extends CommandLineTestCase { 15 | 16 | @Test 17 | public void testClassPrecedesMethod() throws Exception { 18 | assertTrue(ArgumentType.CLASS_ARG.compareTo(ArgumentType.METHOD_ARG) == -1); 19 | // further drive point home - method inserted at index 0, class index 1 20 | List classVsMethod = Arrays.asList(ArgumentType.METHOD_ARG, ArgumentType.CLASS_ARG); 21 | assertEquals(0, classVsMethod.indexOf(ArgumentType.METHOD_ARG)); 22 | assertEquals(1, classVsMethod.indexOf(ArgumentType.CLASS_ARG)); 23 | Collections.sort(classVsMethod); 24 | // now - because of comparable impl was applied, class precedes method - this is necessary 25 | // @ see KoanSuiteRunner.run() 26 | assertEquals(1, classVsMethod.indexOf(ArgumentType.METHOD_ARG)); 27 | assertEquals(0, classVsMethod.indexOf(ArgumentType.CLASS_ARG)); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /koans/app/data/src/java7/AboutDiamondOperator.java: -------------------------------------------------------------------------------- 1 | package java7; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | import static com.sandwich.koan.constant.KoanConstants.__; 10 | import static com.sandwich.util.Assert.assertEquals; 11 | 12 | public class AboutDiamondOperator { 13 | 14 | @Koan 15 | public void diamondOperator() { 16 | String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; 17 | //Generic type of array list inferred - empty <> operator 18 | List animalsList = new ArrayList<>(Arrays.asList(animals)); 19 | assertEquals(animalsList, __); 20 | } 21 | 22 | @Koan 23 | public void diamondOperatorInMethodCall() { 24 | String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; 25 | //type of new ArrayList<>() inferred from method parameter 26 | List animalsList = fill(new ArrayList<>()); 27 | assertEquals(animalsList, __); 28 | } 29 | 30 | private List fill(List list) { 31 | String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; 32 | list.addAll(Arrays.asList(animals)); 33 | return list; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/FileOperation.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | public abstract class FileOperation implements FileAction { 9 | 10 | private List ignoredPaths; 11 | 12 | public FileOperation(String... ignoredPaths){ 13 | this(Arrays.asList(ignoredPaths)); 14 | } 15 | 16 | public FileOperation(List ignoredPaths){ 17 | this.ignoredPaths = ignoredPaths; 18 | } 19 | 20 | public void operate(File file) throws IOException { 21 | if(!isIgnored(file)){ 22 | if(file == null){ 23 | onNull(file); 24 | }else if(!file.exists()){ 25 | onNew(file); 26 | }else if(file.isDirectory()){ 27 | onDirectory(file); 28 | }else{ 29 | onFile(file); 30 | } 31 | } 32 | } 33 | 34 | boolean isIgnored(File file){ 35 | boolean ignore = false; 36 | while(file != null){ 37 | String end = file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf(System.getProperty("file.separator")) + 1); 38 | for(String pathToIgnore: ignoredPaths){ 39 | ignore = end.matches(pathToIgnore); 40 | if(ignore){ 41 | return true; 42 | } 43 | } 44 | file = file.getParentFile(); 45 | } 46 | return ignore; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/SourceAndDestinationFileAction.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | public abstract class SourceAndDestinationFileAction extends ForEachFileAction { 7 | 8 | private final File source; 9 | private final File destination; 10 | 11 | public SourceAndDestinationFileAction(String sourceDir, String destinationDir){ 12 | this(new File(sourceDir), new File(destinationDir)); 13 | } 14 | 15 | public SourceAndDestinationFileAction(File source, File destination){ 16 | this.source = assertIsDirectory(source); 17 | this.destination = assertIsDirectory(destination); 18 | } 19 | 20 | public void operate() throws IOException { 21 | super.operate(source); 22 | } 23 | 24 | public void onFile(File file) throws IOException { 25 | String subDir = file.getAbsolutePath().replace(source.getAbsolutePath(), ""); 26 | File dest = new File(destination.getAbsolutePath() + File.separator + subDir); 27 | sourceToDestination(file, dest); 28 | } 29 | 30 | abstract public void sourceToDestination(File src, File dest) throws IOException; 31 | 32 | private File assertIsDirectory(File file) { 33 | if(file == null || !file.isDirectory()){ 34 | throw new IllegalArgumentException(file + " is not a directory."); 35 | } 36 | return file; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/KoansResultTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.util.Arrays; 6 | 7 | import org.junit.Test; 8 | 9 | import com.sandwich.koan.path.CommandLineTestCase; 10 | import com.sandwich.koan.result.KoanMethodResult; 11 | import com.sandwich.koan.result.KoanSuiteResult; 12 | import com.sandwich.koan.result.KoanSuiteResult.KoanResultBuilder; 13 | import com.sandwich.koan.suite.OneFailingKoan; 14 | 15 | public class KoansResultTest extends CommandLineTestCase { 16 | 17 | @Test 18 | public void testToString() throws Exception { 19 | KoanResultBuilder builder = new KoanResultBuilder() 20 | .remainingCases(Arrays.asList(OneFailingKoan.class.getSimpleName())) 21 | .methodResult(new KoanMethodResult(KoanMethod.getInstance("", OneFailingKoan.class.getDeclaredMethods()[0]), "msg", "2")) 22 | .level("1") 23 | .numberPassing(3); 24 | KoanSuiteResult result = builder.build(); 25 | String string = result.toString(); 26 | assertTrue(string.contains("1")); 27 | assertTrue(string.contains("2")); 28 | assertTrue(string.contains("3")); 29 | assertTrue(string.contains(OneFailingKoan.class.getSimpleName())); 30 | assertTrue(string.contains(OneFailingKoan.class.getDeclaredMethods()[0].getName())); 31 | assertTrue(string.contains("msg")); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/path/xmltransformation/FakeXmlToPathTransformer.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.path.xmltransformation; 2 | 3 | import java.util.Collections; 4 | import java.util.LinkedHashMap; 5 | import java.util.Map; 6 | 7 | import com.sandwich.koan.path.PathToEnlightenment.Path; 8 | 9 | public class FakeXmlToPathTransformer extends XmlToPathTransformerImpl { 10 | 11 | private Map> methodsBySuite; 12 | 13 | @SuppressWarnings("unchecked") 14 | public FakeXmlToPathTransformer() { 15 | this(Collections.EMPTY_MAP); 16 | } 17 | 18 | public FakeXmlToPathTransformer(Map> methodsBySuite){ 19 | this.methodsBySuite = methodsBySuite; 20 | } 21 | 22 | public Map> getMethodsBySuite() { 23 | return methodsBySuite; 24 | } 25 | 26 | public void setMethodsBySuite(Map> methodsBySuite) { 27 | this.methodsBySuite = methodsBySuite; 28 | } 29 | 30 | @Override 31 | public Path transform(){ 32 | Map>> koans = 33 | new LinkedHashMap>>(); 34 | koans.put("test", new LinkedHashMap>(methodsBySuite)); 35 | return new Path(null,koans); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/behavior/Clear.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline.behavior; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import com.sandwich.koan.util.ApplicationUtils; 9 | import com.sandwich.util.io.directories.DirectoryManager; 10 | 11 | public class Clear extends AbstractArgumentBehavior { 12 | 13 | List files = Arrays.asList( 14 | DirectoryManager.getDataFile(), 15 | DirectoryManager.getBinDir(), 16 | DirectoryManager.getDataDir()); 17 | 18 | public void run(String... values) throws Exception { 19 | List unableToDelete = new ArrayList(); 20 | for(String fileName : files){ 21 | File file = new File(fileName); 22 | if(file.exists()){ 23 | if(file.delete()){ 24 | ApplicationUtils.getPresenter().displayMessage(file.getAbsolutePath() + " deleted successfully."); 25 | }else{ 26 | unableToDelete.add(file); 27 | ApplicationUtils.getPresenter().displayError(file.getAbsolutePath() + " was NOT DELETED. Please delete manually."); 28 | } 29 | }else{ 30 | ApplicationUtils.getPresenter().displayMessage(file.getAbsolutePath() + " does not exist. Skipping."); 31 | } 32 | } 33 | if(!unableToDelete.isEmpty()){ 34 | throw new RuntimeException("Unable to delete: "+unableToDelete+" see output for details."); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/filecompiler/CompilationFailureLogger.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io.filecompiler; 2 | 3 | import java.io.File; 4 | 5 | import com.sandwich.util.io.ui.DefaultErrorPresenter; 6 | import com.sandwich.util.io.ui.ErrorPresenter; 7 | 8 | public class CompilationFailureLogger implements CompilationListener { 9 | 10 | private ErrorPresenter presenter; 11 | 12 | public CompilationFailureLogger(){ 13 | this(new DefaultErrorPresenter()); 14 | } 15 | 16 | public CompilationFailureLogger(ErrorPresenter presenter){ 17 | this.presenter = presenter; 18 | } 19 | 20 | public void compilationFailed(File src, String[] command, int exitCode, String errorMessage, Throwable x) { 21 | String lineSeparator = System.getProperty("line.separator"); 22 | presenter.displayError( 23 | lineSeparator + 24 | "*****************************************************************" + lineSeparator + 25 | "Compile Output:" + lineSeparator + 26 | errorMessage.replace(lineSeparator, lineSeparator + " ") + lineSeparator + 27 | "Compiling \"" + src.getAbsolutePath() + "\" failed." + lineSeparator + 28 | "The exit status was: " + exitCode + lineSeparator + 29 | "*****************************************************************" + lineSeparator + 30 | lineSeparator); 31 | } 32 | public void compilationSucceeded(File src, String[] command, String stdIo, Throwable x) { } 33 | } -------------------------------------------------------------------------------- /koans/src/advanced/AboutMocks.java: -------------------------------------------------------------------------------- 1 | package advanced; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.util.Assert.fail; 6 | 7 | public class AboutMocks { 8 | 9 | static interface Collaborator { 10 | public void doBusinessStuff(); 11 | } 12 | 13 | static class ExplosiveCollaborator implements Collaborator { 14 | public void doBusinessStuff() { 15 | fail("Default collaborator's behavior is complicating testing."); 16 | } 17 | } 18 | 19 | static class ClassUnderTest { 20 | Collaborator c; 21 | 22 | public ClassUnderTest() { 23 | // default is to pass a broken Collaborator, test should pass one 24 | // that doesn't throw exception 25 | this(new ExplosiveCollaborator()); 26 | } 27 | 28 | public ClassUnderTest(Collaborator c) { 29 | this.c = c; 30 | } 31 | 32 | public boolean doSomething() { 33 | c.doBusinessStuff(); 34 | return true; 35 | } 36 | } 37 | 38 | @Koan 39 | public void simpleAnonymousMock() { 40 | // HINT: pass a safe Collaborator implementation to constructor 41 | // new ClassUnderTest(new Collaborator(){... it should not be the 42 | // objective of this test to test that collaborator, so replace it 43 | new ClassUnderTest().doSomething(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /koans/app/data/src/advanced/AboutMocks.java: -------------------------------------------------------------------------------- 1 | package advanced; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.util.Assert.fail; 6 | 7 | public class AboutMocks { 8 | 9 | static interface Collaborator { 10 | public void doBusinessStuff(); 11 | } 12 | 13 | static class ExplosiveCollaborator implements Collaborator { 14 | public void doBusinessStuff() { 15 | fail("Default collaborator's behavior is complicating testing."); 16 | } 17 | } 18 | 19 | static class ClassUnderTest { 20 | Collaborator c; 21 | 22 | public ClassUnderTest() { 23 | // default is to pass a broken Collaborator, test should pass one 24 | // that doesn't throw exception 25 | this(new ExplosiveCollaborator()); 26 | } 27 | 28 | public ClassUnderTest(Collaborator c) { 29 | this.c = c; 30 | } 31 | 32 | public boolean doSomething() { 33 | c.doBusinessStuff(); 34 | return true; 35 | } 36 | } 37 | 38 | @Koan 39 | public void simpleAnonymousMock() { 40 | // HINT: pass a safe Collaborator implementation to constructor 41 | // new ClassUnderTest(new Collaborator(){... it should not be the 42 | // objective of this test to test that collaborator, so replace it 43 | new ClassUnderTest().doSomething(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/path/PathToEnlightenmentTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.path; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | 5 | import java.util.Locale; 6 | 7 | import org.junit.Test; 8 | 9 | import com.sandwich.koan.LocaleSwitchingTestCase; 10 | 11 | public class PathToEnlightenmentTest extends LocaleSwitchingTestCase { 12 | 13 | @Test 14 | public void testFallsBackToEnglishXmlWhenNoXmlForLocaleIsFound(){ 15 | Locale.setDefault(Locale.CHINA); 16 | PathToEnlightenment.xmlToPathTransformer = null; 17 | assertNotNull(PathToEnlightenment.getXmlToPathTransformer()); 18 | } 19 | 20 | @Test 21 | public void testEnglishXmlWhenXmlForLocaleIsFound_eventIsNotLogged(){ 22 | Locale.setDefault(Locale.US); 23 | PathToEnlightenment.xmlToPathTransformer = null; 24 | assertLogged(PathToEnlightenment.class.getName(), new RBSensitiveLoggerExpectation(){ 25 | protected boolean isLogCallRequired(){ 26 | return false; 27 | } 28 | }); 29 | } 30 | 31 | @Test 32 | public void testEnglishXmlWhenXmlForLocaleIsFound(){ 33 | Locale.setDefault(Locale.US); 34 | PathToEnlightenment.xmlToPathTransformer = null; 35 | assertNotNull(PathToEnlightenment.getXmlToPathTransformer()); 36 | } 37 | 38 | private class RBSensitiveLoggerExpectation extends LoggerExpectation { 39 | @Override 40 | protected void invokeImplementation() { 41 | PathToEnlightenment.getXmlToPathTransformer(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /koans/src/java8/AboutDefaultMethods.java: -------------------------------------------------------------------------------- 1 | package java8; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutDefaultMethods { 9 | 10 | @Koan 11 | public void interfaceDefaultMethod() { 12 | StringUtil stringUtil = new StringUtil() { 13 | @Override 14 | public String reverse(String s) { 15 | return new StringBuilder(s).reverse().toString(); 16 | } 17 | }; 18 | String capitalizedReversed = stringUtil.capitalize( 19 | stringUtil.reverse("gnirut")); 20 | assertEquals(capitalizedReversed, __); 21 | } 22 | 23 | @Koan 24 | public void interfaceStaticMethod() { 25 | assertEquals(StringUtil.enclose("me"), __); 26 | } 27 | 28 | interface StringUtil { 29 | 30 | //static method in interface 31 | static String enclose(String in) { 32 | return "[" + in + "]"; 33 | } 34 | 35 | String reverse(String s); 36 | 37 | //interface can contain non-abstract method implementations marked by "default" keyword 38 | default String capitalize(String s) { 39 | return s.toUpperCase(); 40 | } 41 | 42 | default String capitalizeFirst(String s) { 43 | return s.substring(0, 1).toUpperCase() + s.substring(1); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /koans/app/data/src/java8/AboutDefaultMethods.java: -------------------------------------------------------------------------------- 1 | package java8; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutDefaultMethods { 9 | 10 | @Koan 11 | public void interfaceDefaultMethod() { 12 | StringUtil stringUtil = new StringUtil() { 13 | @Override 14 | public String reverse(String s) { 15 | return new StringBuilder(s).reverse().toString(); 16 | } 17 | }; 18 | String capitalizedReversed = stringUtil.capitalize( 19 | stringUtil.reverse("gnirut")); 20 | assertEquals(capitalizedReversed, __); 21 | } 22 | 23 | @Koan 24 | public void interfaceStaticMethod() { 25 | assertEquals(StringUtil.enclose("me"), __); 26 | } 27 | 28 | interface StringUtil { 29 | 30 | //static method in interface 31 | static String enclose(String in) { 32 | return "[" + in + "]"; 33 | } 34 | 35 | String reverse(String s); 36 | 37 | //interface can contain non-abstract method implementations marked by "default" keyword 38 | default String capitalize(String s) { 39 | return s.toUpperCase(); 40 | } 41 | 42 | default String capitalizeFirst(String s) { 43 | return s.substring(0, 1).toUpperCase() + s.substring(1); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/util/SimpleEntry.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util; 2 | 3 | import java.util.Map.Entry; 4 | 5 | /** 6 | * Why on earth this wasn't around when JDK5 was released is just as bad as 7 | * string.isEmpty()'s absence jeesh! 8 | * 9 | * @param 10 | * @param 11 | */ 12 | public class SimpleEntry implements Entry{ 13 | 14 | private K k; 15 | private V v; 16 | 17 | public SimpleEntry(K k, V v){ 18 | this.k = k; 19 | this.v = v; 20 | } 21 | 22 | public K getKey() { 23 | return k; 24 | } 25 | 26 | public V getValue() { 27 | return v; 28 | } 29 | 30 | public V setValue(V value) { 31 | V tmp = v; 32 | v = value; 33 | return tmp; 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | final int prime = 31; 39 | int result = 1; 40 | result = prime * result + ((k == null) ? 0 : k.hashCode()); 41 | result = prime * result + ((v == null) ? 0 : v.hashCode()); 42 | return result; 43 | } 44 | 45 | @Override 46 | public boolean equals(Object obj) { 47 | if (this == obj) 48 | return true; 49 | if (obj == null) 50 | return false; 51 | if(!Entry.class.isAssignableFrom(SimpleEntry.class)){ 52 | return false; 53 | } 54 | Entry other = (Entry) obj; 55 | if (k == null) { 56 | if (other.getKey() != null) 57 | return false; 58 | } else if (!k.equals(other.getKey())) 59 | return false; 60 | if (v == null) { 61 | if (other.getValue() != null) 62 | return false; 63 | } else if (!v.equals(other.getValue())) 64 | return false; 65 | return true; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutArithmeticOperators.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutArithmeticOperators { 9 | 10 | @Koan 11 | public void simpleOperations() { 12 | assertEquals(1, 1); 13 | assertEquals(1 + 1, 2); 14 | assertEquals(2 + 3 * 4, 14); 15 | assertEquals((2 + 3) * 4, 20); 16 | assertEquals(2 * 3 + 4, 10); 17 | assertEquals(2 - 3 + 4, 3); 18 | assertEquals(2 + 4 / 2, 4); 19 | assertEquals((2 + 4) / 2, 3); 20 | } 21 | 22 | @Koan 23 | public void notSoSimpleOperations() { 24 | assertEquals(1 / 2, 0); 25 | assertEquals(3 / 2, 1); 26 | assertEquals(1 % 2, 1); 27 | assertEquals(3 % 2, 1); 28 | } 29 | 30 | @Koan 31 | public void minusMinusVariableMinusMinus() { 32 | int i = 1; 33 | assertEquals(--i, 0); 34 | assertEquals(i, 0); 35 | assertEquals(i--, 0); 36 | assertEquals(i, -1); 37 | } 38 | 39 | @Koan 40 | public void plusPlusVariablePlusPlus() { 41 | int i = 1; 42 | assertEquals(++i, 2); 43 | assertEquals(i, 2); 44 | assertEquals(i++, 2); 45 | assertEquals(i, 3); 46 | } 47 | 48 | @Koan 49 | public void timesAndDivInPlace() { 50 | int i = 1; 51 | i *= 2; 52 | assertEquals(i, 2); 53 | i /= 2; 54 | assertEquals(i, 1); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutArithmeticOperators.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutArithmeticOperators { 9 | 10 | @Koan 11 | public void simpleOperations() { 12 | assertEquals(1, __); 13 | assertEquals(1 + 1, __); 14 | assertEquals(2 + 3 * 4, __); 15 | assertEquals((2 + 3) * 4, __); 16 | assertEquals(2 * 3 + 4, __); 17 | assertEquals(2 - 3 + 4, __); 18 | assertEquals(2 + 4 / 2, __); 19 | assertEquals((2 + 4) / 2, __); 20 | } 21 | 22 | @Koan 23 | public void notSoSimpleOperations() { 24 | assertEquals(1 / 2, __); 25 | assertEquals(3 / 2, __); 26 | assertEquals(1 % 2, __); 27 | assertEquals(3 % 2, __); 28 | } 29 | 30 | @Koan 31 | public void minusMinusVariableMinusMinus() { 32 | int i = 1; 33 | assertEquals(--i, __); 34 | assertEquals(i, __); 35 | assertEquals(i--, __); 36 | assertEquals(i, __); 37 | } 38 | 39 | @Koan 40 | public void plusPlusVariablePlusPlus() { 41 | int i = 1; 42 | assertEquals(++i, __); 43 | assertEquals(i, __); 44 | assertEquals(i++, __); 45 | assertEquals(i, __); 46 | } 47 | 48 | @Koan 49 | public void timesAndDivInPlace() { 50 | int i = 1; 51 | i *= 2; 52 | assertEquals(i, __); 53 | i /= 2; 54 | assertEquals(i, __); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/directories/DirectorySet.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io.directories; 2 | 3 | import java.io.File; 4 | 5 | abstract public class DirectorySet { 6 | 7 | private static final String BIN_DIR = "bin"; 8 | private static final String APP_DIR = "app"; 9 | private static final String LIB_DIR = "lib"; 10 | private static final String DATA_DIR = "data"; 11 | private static final String I18N_DIR = "i18n"; 12 | private static final String CONFIG_DIR = "config"; 13 | private static final String BASE_DIR = createBaseDir(); 14 | 15 | abstract String getSourceDir(); 16 | abstract String getProjectDir(); 17 | 18 | public String getBaseDir(){ 19 | return BASE_DIR; 20 | } 21 | 22 | public String getBinaryDir(){ 23 | return BIN_DIR; 24 | } 25 | 26 | public String getLibrariesDir(){ 27 | return LIB_DIR; 28 | } 29 | 30 | public String getI18nDir(){ 31 | return I18N_DIR; 32 | } 33 | 34 | public String getAppDir(){ 35 | return APP_DIR; 36 | } 37 | 38 | public String getConfigDir() { 39 | return CONFIG_DIR; 40 | } 41 | 42 | public String getDataDir(){ 43 | return DATA_DIR; 44 | } 45 | 46 | private static String createBaseDir() { 47 | String baseDir = System.getProperty("application.basedir"); 48 | if(baseDir != null){ 49 | if(baseDir.startsWith("\"")){ 50 | baseDir = baseDir.substring(1); 51 | } 52 | if(baseDir.endsWith("\"")){ 53 | baseDir = baseDir.substring(0, baseDir.length() - 1); 54 | } 55 | return new File(baseDir).getParentFile().getAbsolutePath(); 56 | } 57 | return new File("").getAbsoluteFile().getParentFile().getAbsolutePath(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/KoanComparator.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util; 2 | 3 | import java.util.Comparator; 4 | import java.util.logging.Logger; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | import com.sandwich.koan.KoanMethod; 9 | import com.sandwich.util.io.directories.DirectoryManager; 10 | import com.sandwich.util.io.filecompiler.FileCompiler; 11 | 12 | public class KoanComparator implements Comparator { 13 | 14 | public int compare(KoanMethod arg0, KoanMethod arg1) { 15 | Class declaringClass0 = arg0.getMethod().getDeclaringClass(); 16 | Class declaringClass1 = arg1.getMethod().getDeclaringClass(); 17 | if(declaringClass0 != declaringClass1){ 18 | Logger.getAnonymousLogger().severe("no idea how to handle comparing the classes: " + declaringClass0 + " and: "+declaringClass1); 19 | return 0; 20 | } 21 | String contentsOfOriginalJavaFile = FileCompiler.getContentsOfJavaFile(DirectoryManager.getSourceDir(), declaringClass0.getName()); 22 | String pattern = ".*\\s%s(\\(|\\s*\\))"; 23 | Integer index0 = indexOfMatch(contentsOfOriginalJavaFile, String.format(pattern, arg0.getMethod().getName())); 24 | Integer index1 = indexOfMatch(contentsOfOriginalJavaFile, String.format(pattern, arg1.getMethod().getName())); 25 | return index0.compareTo(index1); 26 | } 27 | 28 | /* 29 | * TODO: This is utility code... 30 | */ 31 | private int indexOfMatch(String inputString, String pattern) { 32 | Pattern p = Pattern.compile(pattern); 33 | Matcher m = p.matcher(inputString); 34 | if (m.find()) { 35 | return m.start(); 36 | } 37 | return -1; 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /koans/src/java7/AboutRequireNotNull.java: -------------------------------------------------------------------------------- 1 | package java7; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.Objects; 6 | 7 | import static com.sandwich.koan.constant.KoanConstants.__; 8 | import static com.sandwich.util.Assert.assertEquals; 9 | 10 | public class AboutRequireNotNull { 11 | 12 | @Koan 13 | public void failArgumentValidationWithRequireNotNull() { 14 | // This koan demonstrates the use of Objects.requireNotNull 15 | // in place of traditional argument validation using exceptions 16 | String s = ""; 17 | try { 18 | s += validateUsingRequireNotNull(null); 19 | } catch (NullPointerException ex) { 20 | s = "caught a NullPointerException"; 21 | } 22 | assertEquals(s, __); 23 | } 24 | 25 | @Koan 26 | public void passArgumentValidationWithRequireNotNull() { 27 | // This koan demonstrates the use of Objects.requireNotNull 28 | // in place of traditional argument validation using exceptions 29 | String s = ""; 30 | try { 31 | s += validateUsingRequireNotNull("valid"); 32 | } catch (NullPointerException ex) { 33 | s = "caught a NullPointerException"; 34 | } 35 | assertEquals(s, __); 36 | } 37 | 38 | private int validateUsingRequireNotNull(String str) { 39 | // If you're only concerned with null values requireNotNull 40 | // is concise and the point of the NullPointerException it 41 | // throws is clear, though you can optionally provide a 42 | // description as well 43 | return Objects.requireNonNull(str).length(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /koans/app/data/src/java7/AboutRequireNotNull.java: -------------------------------------------------------------------------------- 1 | package java7; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.Objects; 6 | 7 | import static com.sandwich.koan.constant.KoanConstants.__; 8 | import static com.sandwich.util.Assert.assertEquals; 9 | 10 | public class AboutRequireNotNull { 11 | 12 | @Koan 13 | public void failArgumentValidationWithRequireNotNull() { 14 | // This koan demonstrates the use of Objects.requireNotNull 15 | // in place of traditional argument validation using exceptions 16 | String s = ""; 17 | try { 18 | s += validateUsingRequireNotNull(null); 19 | } catch (NullPointerException ex) { 20 | s = "caught a NullPointerException"; 21 | } 22 | assertEquals(s, __); 23 | } 24 | 25 | @Koan 26 | public void passArgumentValidationWithRequireNotNull() { 27 | // This koan demonstrates the use of Objects.requireNotNull 28 | // in place of traditional argument validation using exceptions 29 | String s = ""; 30 | try { 31 | s += validateUsingRequireNotNull("valid"); 32 | } catch (NullPointerException ex) { 33 | s = "caught a NullPointerException"; 34 | } 35 | assertEquals(s, __); 36 | } 37 | 38 | private int validateUsingRequireNotNull(String str) { 39 | // If you're only concerned with null values requireNotNull 40 | // is concise and the point of the NullPointerException it 41 | // throws is clear, though you can optionally provide a 42 | // description as well 43 | return Objects.requireNonNull(str).length(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /koans/src/intermediate/AboutAutoboxing.java: -------------------------------------------------------------------------------- 1 | package intermediate; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import static com.sandwich.koan.constant.KoanConstants.__; 9 | import static com.sandwich.util.Assert.assertEquals; 10 | 11 | public class AboutAutoboxing { 12 | 13 | @Koan 14 | public void addPrimitivesToCollection() { 15 | List list = new ArrayList(); 16 | list.add(0, new Integer(42)); 17 | assertEquals(list.get(0), __); 18 | } 19 | 20 | @Koan 21 | public void addPrimitivesToCollectionWithAutoBoxing() { 22 | List list = new ArrayList(); 23 | list.add(0, 42); 24 | assertEquals(list.get(0), __); 25 | } 26 | 27 | @Koan 28 | public void migrateYourExistingCodeToAutoBoxingWithoutFear() { 29 | List list = new ArrayList(); 30 | list.add(0, new Integer(42)); 31 | assertEquals(list.get(0), __); 32 | 33 | list.add(1, 84); 34 | assertEquals(list.get(1), __); 35 | } 36 | 37 | @Koan 38 | public void allPrimitivesCanBeAutoboxed() { 39 | List doubleList = new ArrayList(); 40 | doubleList.add(0, new Double(42)); 41 | assertEquals(doubleList.get(0), __); 42 | 43 | List longList = new ArrayList(); 44 | longList.add(0, new Long(42)); 45 | assertEquals(longList.get(0), __); 46 | 47 | List characterList = new ArrayList(); 48 | characterList.add(0, new Character('z')); 49 | assertEquals(characterList.get(0), __); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /koans/app/data/src/intermediate/AboutAutoboxing.java: -------------------------------------------------------------------------------- 1 | package intermediate; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import static com.sandwich.koan.constant.KoanConstants.__; 9 | import static com.sandwich.util.Assert.assertEquals; 10 | 11 | public class AboutAutoboxing { 12 | 13 | @Koan 14 | public void addPrimitivesToCollection() { 15 | List list = new ArrayList(); 16 | list.add(0, new Integer(42)); 17 | assertEquals(list.get(0), __); 18 | } 19 | 20 | @Koan 21 | public void addPrimitivesToCollectionWithAutoBoxing() { 22 | List list = new ArrayList(); 23 | list.add(0, 42); 24 | assertEquals(list.get(0), __); 25 | } 26 | 27 | @Koan 28 | public void migrateYourExistingCodeToAutoBoxingWithoutFear() { 29 | List list = new ArrayList(); 30 | list.add(0, new Integer(42)); 31 | assertEquals(list.get(0), __); 32 | 33 | list.add(1, 84); 34 | assertEquals(list.get(1), __); 35 | } 36 | 37 | @Koan 38 | public void allPrimitivesCanBeAutoboxed() { 39 | List doubleList = new ArrayList(); 40 | doubleList.add(0, new Double(42)); 41 | assertEquals(doubleList.get(0), __); 42 | 43 | List longList = new ArrayList(); 44 | longList.add(0, new Long(42)); 45 | assertEquals(longList.get(0), __); 46 | 47 | List characterList = new ArrayList(); 48 | characterList.add(0, new Character('z')); 49 | assertEquals(characterList.get(0), __); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutMethodPreference.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutMethodPreference { 9 | 10 | class A { 11 | public String doStuff(int i) { 12 | return "int"; 13 | } 14 | 15 | public String doStuff(Integer i) { 16 | return "Integer"; 17 | } 18 | 19 | public String doStuff(Object i) { 20 | return "Object"; 21 | } 22 | 23 | public String doStuff(int... i) { 24 | return "int vararg"; 25 | } 26 | } 27 | 28 | @Koan 29 | public void methodPreferenceInt() { 30 | assertEquals(new A().doStuff(1), __); 31 | } 32 | 33 | @Koan 34 | public void methodPreferenceInteger() { 35 | assertEquals(new A().doStuff(Integer.valueOf(1)), __); 36 | } 37 | 38 | @Koan 39 | public void methodPreferenceLong() { 40 | long l = 1; 41 | assertEquals(new A().doStuff(l), __); 42 | } 43 | 44 | @Koan 45 | public void methodPreferenceBoxedLong() { 46 | Long l = Long.valueOf(1); 47 | assertEquals(new A().doStuff(l), __); 48 | } 49 | 50 | @Koan 51 | public void methodPreferenceDouble() { 52 | Double l = Double.valueOf(1); 53 | assertEquals(new A().doStuff(l), __); 54 | } 55 | 56 | @Koan 57 | public void methodPreferenceMore() { 58 | // What happens if you change 'Integer' to 'Double' 59 | // Does this explain 'methodPreferenceDouble'? 60 | // Think about why this happens? 61 | assertEquals(new A().doStuff(1, Integer.valueOf(2)), __); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutMethodPreference.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutMethodPreference { 9 | 10 | class A { 11 | public String doStuff(int i) { 12 | return "int"; 13 | } 14 | 15 | public String doStuff(Integer i) { 16 | return "Integer"; 17 | } 18 | 19 | public String doStuff(Object i) { 20 | return "Object"; 21 | } 22 | 23 | public String doStuff(int... i) { 24 | return "int vararg"; 25 | } 26 | } 27 | 28 | @Koan 29 | public void methodPreferenceInt() { 30 | assertEquals(new A().doStuff(1), __); 31 | } 32 | 33 | @Koan 34 | public void methodPreferenceInteger() { 35 | assertEquals(new A().doStuff(Integer.valueOf(1)), __); 36 | } 37 | 38 | @Koan 39 | public void methodPreferenceLong() { 40 | long l = 1; 41 | assertEquals(new A().doStuff(l), __); 42 | } 43 | 44 | @Koan 45 | public void methodPreferenceBoxedLong() { 46 | Long l = Long.valueOf(1); 47 | assertEquals(new A().doStuff(l), __); 48 | } 49 | 50 | @Koan 51 | public void methodPreferenceDouble() { 52 | Double l = Double.valueOf(1); 53 | assertEquals(new A().doStuff(l), __); 54 | } 55 | 56 | @Koan 57 | public void methodPreferenceMore() { 58 | // What happens if you change 'Integer' to 'Double' 59 | // Does this explain 'methodPreferenceDouble'? 60 | // Think about why this happens? 61 | assertEquals(new A().doStuff(1, Integer.valueOf(2)), __); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/LocaleSwitchingTestCase.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan; 2 | 3 | import static org.junit.Assert.fail; 4 | 5 | import java.util.Locale; 6 | import java.util.logging.Handler; 7 | import java.util.logging.LogRecord; 8 | import java.util.logging.Logger; 9 | 10 | import org.junit.After; 11 | import org.junit.Before; 12 | 13 | public class LocaleSwitchingTestCase { 14 | 15 | private Locale defaultLocale; 16 | 17 | @Before 18 | public void setUp(){ 19 | this.defaultLocale = Locale.getDefault(); 20 | } 21 | 22 | @After 23 | public void tearDown(){ 24 | Locale.setDefault(defaultLocale); 25 | } 26 | 27 | protected void assertLogged(String loggerName, final LoggerExpectation loggerExpectation) { 28 | Logger logger = Logger.getLogger(loggerName); 29 | final boolean[] called = new boolean[]{false}; 30 | final Handler handler = new Handler(){ 31 | @Override public void close() throws SecurityException {} 32 | @Override public void flush() {} 33 | @Override public void publish(final LogRecord record) { 34 | loggerExpectation.logCalled(record); 35 | called[0] = true; 36 | } 37 | }; 38 | logger.addHandler(handler); 39 | try{ 40 | loggerExpectation.invokeImplementation(); 41 | if(loggerExpectation.isLogCallRequired() && !called[0]){ 42 | fail("The logger was never called, it should have been."); 43 | } 44 | }finally{ 45 | logger.removeHandler(handler); 46 | } 47 | } 48 | 49 | protected class LoggerExpectation { 50 | protected void logCalled(LogRecord record){ 51 | fail("Unexpected logging event: "+record); 52 | } 53 | protected boolean isLogCallRequired(){ 54 | return true; 55 | } 56 | protected void invokeImplementation(){ 57 | fail("No invocation definition defined."); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutVarArgs.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutVarArgs { 9 | 10 | class ExampleClass { 11 | public boolean canBeTreatedAsArray(Integer... arguments) { 12 | return arguments instanceof Integer[]; 13 | } 14 | 15 | public int getLength(Integer... arguments) { 16 | return arguments.length; 17 | } 18 | 19 | public String verboseLength(String prefix, Object... arguments) { 20 | return prefix + arguments.length; 21 | } 22 | 23 | // ******* 24 | // The following methods won't compile because Java only permits varargs as last argument 25 | // ******* 26 | // public String invalidMethodDeclaration(String... arguments, String... otherArguments) { return ""; } 27 | // public String otherInvalidMethodDeclaration(String... arguments, String otherArgument) { return ""; } 28 | } 29 | 30 | @Koan 31 | public void varArgsCanBeTreatedAsArrays() { 32 | assertEquals(new ExampleClass().canBeTreatedAsArray(1, 2, 3), __); 33 | } 34 | 35 | @Koan 36 | public void youCanPassInAsManyArgumentsAsYouLike() { 37 | assertEquals(new ExampleClass().getLength(1, 2, 3), __); 38 | assertEquals(new ExampleClass().getLength(1, 2, 3, 4, 5, 6, 7, 8), __); 39 | } 40 | 41 | @Koan 42 | public void youCanPassInZeroArgumentsIfYouLike() { 43 | assertEquals(new ExampleClass().getLength(), __); 44 | } 45 | 46 | @Koan 47 | public void youCanHaveOtherTypesInTheMethodSignature() { 48 | assertEquals(new ExampleClass().verboseLength("This is how many items were passed in: ", 1, 2, 3, 4), __); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutVarArgs.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutVarArgs { 9 | 10 | class ExampleClass { 11 | public boolean canBeTreatedAsArray(Integer... arguments) { 12 | return arguments instanceof Integer[]; 13 | } 14 | 15 | public int getLength(Integer... arguments) { 16 | return arguments.length; 17 | } 18 | 19 | public String verboseLength(String prefix, Object... arguments) { 20 | return prefix + arguments.length; 21 | } 22 | 23 | // ******* 24 | // The following methods won't compile because Java only permits varargs as last argument 25 | // ******* 26 | // public String invalidMethodDeclaration(String... arguments, String... otherArguments) { return ""; } 27 | // public String otherInvalidMethodDeclaration(String... arguments, String otherArgument) { return ""; } 28 | } 29 | 30 | @Koan 31 | public void varArgsCanBeTreatedAsArrays() { 32 | assertEquals(new ExampleClass().canBeTreatedAsArray(1, 2, 3), __); 33 | } 34 | 35 | @Koan 36 | public void youCanPassInAsManyArgumentsAsYouLike() { 37 | assertEquals(new ExampleClass().getLength(1, 2, 3), __); 38 | assertEquals(new ExampleClass().getLength(1, 2, 3, 4, 5, 6, 7, 8), __); 39 | } 40 | 41 | @Koan 42 | public void youCanPassInZeroArgumentsIfYouLike() { 43 | assertEquals(new ExampleClass().getLength(), __); 44 | } 45 | 46 | @Koan 47 | public void youCanHaveOtherTypesInTheMethodSignature() { 48 | assertEquals(new ExampleClass().verboseLength("This is how many items were passed in: ", 1, 2, 3, 4), __); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutEquality.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutEquality { 9 | 10 | @Koan 11 | public void doubleEqualsTestsIfTwoObjectsAreTheSame() { 12 | Object object = new Object(); 13 | Object sameObject = object; 14 | assertEquals(object == sameObject, __); 15 | assertEquals(object == new Object(), __); 16 | } 17 | 18 | @Koan 19 | public void equalsMethodByDefaultTestsIfTwoObjectsAreTheSame() { 20 | Object object = new Object(); 21 | assertEquals(object.equals(object), __); 22 | assertEquals(object.equals(new Object()), __); 23 | } 24 | 25 | @Koan 26 | public void equalsMethodCanBeChangedBySubclassesToTestsIfTwoObjectsAreEqual() { 27 | Object object = new Integer(1); 28 | assertEquals(object.equals(object), __); 29 | assertEquals(object.equals(new Integer(1)), __); 30 | // Note: This means that for the class 'Object' there is no difference between 'equal' and 'same' 31 | // but for the class 'Integer' there is difference - see below 32 | } 33 | 34 | @Koan 35 | public void equalsMethodCanBeChangedBySubclassesToTestsIfTwoObjectsAreEqualExample() { 36 | Integer value1 = new Integer(4); 37 | Integer value2 = new Integer(2 + 2); 38 | assertEquals(value1.equals(value2), __); 39 | assertEquals(value1, __); 40 | } 41 | 42 | @Koan 43 | public void objectsNeverEqualNull() { 44 | assertEquals(new Object().equals(null), __); 45 | } 46 | 47 | @Koan 48 | public void objectsEqualThemselves() { 49 | Object obj = new Object(); 50 | assertEquals(obj.equals(obj), __); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutEquality.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutEquality { 9 | 10 | @Koan 11 | public void doubleEqualsTestsIfTwoObjectsAreTheSame() { 12 | Object object = new Object(); 13 | Object sameObject = object; 14 | assertEquals(object == sameObject, true); 15 | assertEquals(object == new Object(), false); 16 | } 17 | 18 | @Koan 19 | public void equalsMethodByDefaultTestsIfTwoObjectsAreTheSame() { 20 | Object object = new Object(); 21 | assertEquals(object.equals(object), true); 22 | assertEquals(object.equals(new Object()), false); 23 | } 24 | 25 | @Koan 26 | public void equalsMethodCanBeChangedBySubclassesToTestsIfTwoObjectsAreEqual() { 27 | Object object = new Integer(1); 28 | assertEquals(object.equals(object), true); 29 | assertEquals(object.equals(new Integer(1)), true); 30 | // Note: This means that for the class 'Object' there is no difference between 'equal' and 'same' 31 | // but for the class 'Integer' there is difference - see below 32 | } 33 | 34 | @Koan 35 | public void equalsMethodCanBeChangedBySubclassesToTestsIfTwoObjectsAreEqualExample() { 36 | Integer value1 = new Integer(4); 37 | Integer value2 = new Integer(2 + 2); 38 | assertEquals(value1.equals(value2), true); 39 | assertEquals(value1, 4); 40 | } 41 | 42 | @Koan 43 | public void objectsNeverEqualNull() { 44 | assertEquals(new Object().equals(null), false); 45 | } 46 | 47 | @Koan 48 | public void objectsEqualThemselves() { 49 | Object obj = new Object(); 50 | assertEquals(obj.equals(obj), true); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /koans/app/config/PathToEnlightenment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutEnums.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutEnums { 9 | 10 | 11 | enum Colors { 12 | Red, Blue, Green, Yellow // what happens if you add a ; here? 13 | // What happens if you type Red() instead? 14 | } 15 | 16 | @Koan 17 | public void basicEnums() { 18 | Colors blue = Colors.Blue; 19 | assertEquals(blue == Colors.Blue, __); 20 | assertEquals(blue == Colors.Red, __); 21 | assertEquals(blue instanceof Colors, __); 22 | } 23 | 24 | @Koan 25 | public void basicEnumsAccess() { 26 | Colors[] colorArray = Colors.values(); 27 | assertEquals(colorArray[2], __); 28 | } 29 | 30 | enum SkatSuits { 31 | Clubs(12), Spades(11), Hearts(10), Diamonds(9); 32 | 33 | SkatSuits(int v) { 34 | value = v; 35 | } 36 | 37 | private int value; 38 | } 39 | 40 | @Koan 41 | public void enumsWithAttributes() { 42 | // value is private but we still can access it. Why? 43 | // Try moving the enum outside the AboutEnum class... What do you expect? 44 | // What happens? 45 | assertEquals(SkatSuits.Clubs.value > SkatSuits.Spades.value, __); 46 | } 47 | 48 | enum OpticalMedia { 49 | CD(650), DVD(4300), BluRay(50000); 50 | 51 | OpticalMedia(int c) { 52 | capacityInMegaBytes = c; 53 | } 54 | 55 | int capacityInMegaBytes; 56 | 57 | int getCoolnessFactor() { 58 | return (capacityInMegaBytes - 1000) * 10; 59 | } 60 | } 61 | 62 | @Koan 63 | public void enumsWithMethods() { 64 | assertEquals(OpticalMedia.CD.getCoolnessFactor(), __); 65 | assertEquals(OpticalMedia.BluRay.getCoolnessFactor(), __); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutEnums.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutEnums { 9 | 10 | 11 | enum Colors { 12 | Red, Blue, Green, Yellow // what happens if you add a ; here? 13 | // What happens if you type Red() instead? 14 | } 15 | 16 | @Koan 17 | public void basicEnums() { 18 | Colors blue = Colors.Blue; 19 | assertEquals(blue == Colors.Blue, __); 20 | assertEquals(blue == Colors.Red, __); 21 | assertEquals(blue instanceof Colors, __); 22 | } 23 | 24 | @Koan 25 | public void basicEnumsAccess() { 26 | Colors[] colorArray = Colors.values(); 27 | assertEquals(colorArray[2], __); 28 | } 29 | 30 | enum SkatSuits { 31 | Clubs(12), Spades(11), Hearts(10), Diamonds(9); 32 | 33 | SkatSuits(int v) { 34 | value = v; 35 | } 36 | 37 | private int value; 38 | } 39 | 40 | @Koan 41 | public void enumsWithAttributes() { 42 | // value is private but we still can access it. Why? 43 | // Try moving the enum outside the AboutEnum class... What do you expect? 44 | // What happens? 45 | assertEquals(SkatSuits.Clubs.value > SkatSuits.Spades.value, __); 46 | } 47 | 48 | enum OpticalMedia { 49 | CD(650), DVD(4300), BluRay(50000); 50 | 51 | OpticalMedia(int c) { 52 | capacityInMegaBytes = c; 53 | } 54 | 55 | int capacityInMegaBytes; 56 | 57 | int getCoolnessFactor() { 58 | return (capacityInMegaBytes - 1000) * 10; 59 | } 60 | } 61 | 62 | @Koan 63 | public void enumsWithMethods() { 64 | assertEquals(OpticalMedia.CD.getCoolnessFactor(), __); 65 | assertEquals(OpticalMedia.BluRay.getCoolnessFactor(), __); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/path/xmltransformation/KoanElementAttributes.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.path.xmltransformation; 2 | 3 | 4 | public class KoanElementAttributes{ 5 | 6 | public String name, displayIncompleteKoanException, className; 7 | 8 | public KoanElementAttributes(String name, String displayIncompleteKoanException, String className){ 9 | this.name = name; 10 | this.displayIncompleteKoanException = displayIncompleteKoanException; 11 | this.className = className; 12 | } 13 | 14 | @Override 15 | public int hashCode() { 16 | final int prime = 31; 17 | int result = 1; 18 | result = prime * result 19 | + ((className == null) ? 0 : className.hashCode()); 20 | result = prime 21 | * result 22 | + ((displayIncompleteKoanException == null) ? 0 23 | : displayIncompleteKoanException.hashCode()); 24 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 25 | return result; 26 | } 27 | 28 | @Override 29 | public boolean equals(Object obj) { 30 | if (this == obj) 31 | return true; 32 | if (obj == null) 33 | return false; 34 | if (getClass() != obj.getClass()) 35 | return false; 36 | KoanElementAttributes other = (KoanElementAttributes) obj; 37 | if (className == null) { 38 | if (other.className != null) 39 | return false; 40 | } else if (!className.equals(other.className)) 41 | return false; 42 | if (displayIncompleteKoanException == null) { 43 | if (other.displayIncompleteKoanException != null) 44 | return false; 45 | } else if (!displayIncompleteKoanException 46 | .equals(other.displayIncompleteKoanException)) 47 | return false; 48 | if (name == null) { 49 | if (other.name != null) 50 | return false; 51 | } else if (!name.equals(other.name)) 52 | return false; 53 | return true; 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "KoanElementAttributes [name=" + name 59 | + ", displayIncompleteKoanException=" 60 | + displayIncompleteKoanException + ", className=" + className 61 | + "]"; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java Koans 2 | 3 | [![Build Status](https://travis-ci.org/matyb/java-koans.png?branch=master)](https://travis-ci.org/matyb/java-koans) 4 | 5 | Running Instructions: 6 | ===================== 7 | * Download and unarchive the contents of the most recent java-koans in development from: 8 | https://github.com/matyb/java-koans/archive/master.zip 9 | * Open a terminal and cd to the directory you unarchived: 10 | ```cd ``` 11 | * Within it you'll find: 12 | * *koans*: this directory contains the application and its lessons, it is all that is needed to advance through the koans themselves and **it can be distributed independently** 13 | * *lib*: this directory contains the code the koans engine is comprised of and built with 14 | * *gradle*: wrapper for build library used to build koans source, setup project files in eclipse/idea, run tests, etc. you probably don't need to touch anything in here 15 | * Change directory to the koans directory: ```cd koans``` 16 | * If you are using windows enter: ```run.bat``` or ```./run.sh``` if you are using Mac or Linux 17 | 18 | Developing a Koan: 19 | ================== 20 | * Follow any of the existing koans as an example to create a new class with koan methods (indicated by the @Koan annotation, they're public and specify no arguments) 21 | * Define the order the koan suite (if it's new) will run in the koans/app/config/PathToEnlightenment.xml file 22 | * [Override the lesson text](https://github.com/matyb/java-koans/blob/master/koans/app/config/i18n/messages_en.properties#L1) when it fails (default is expected 'X' found 'Y') 23 | * Optionally you may use dynamic content in your lesson, examples are located in the XmlVariableInjector class (and Test) and the AboutKoans.java file 24 | 25 | Something's wrong: 26 | ================== 27 | * If the koans app is constantly timing out compiling a koan, your computer may be too slow to compile the koan classes with the default timeout value. Update the compile_timeout_in_ms property in koans/app/config/config.properties with a larger value and try again. 28 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutObjects.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.text.MessageFormat; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import static com.sandwich.koan.constant.KoanConstants.__; 10 | import static com.sandwich.util.Assert.assertEquals; 11 | 12 | public class AboutObjects { 13 | 14 | @Koan 15 | public void newObjectInstancesCanBeCreatedDirectly() { 16 | assertEquals(new Object() instanceof Object, __); 17 | } 18 | 19 | @Koan 20 | public void allClassesInheritFromObject() { 21 | class Foo { 22 | } 23 | 24 | Class[] ancestors = getAncestors(new Foo()); 25 | assertEquals(ancestors[0], __); 26 | assertEquals(ancestors[1], __); 27 | } 28 | 29 | @Koan 30 | public void objectToString() { 31 | Object object = new Object(); 32 | // TODO: Why is it best practice to ALWAYS override toString? 33 | String expectedToString = MessageFormat.format("{0}@{1}", Object.class.getName(), Integer.toHexString(object.hashCode())); 34 | assertEquals(expectedToString, __); // hint: object.toString() 35 | } 36 | 37 | @Koan 38 | public void toStringConcatenates() { 39 | final String string = "ha"; 40 | Object object = new Object() { 41 | @Override 42 | public String toString() { 43 | return string; 44 | } 45 | }; 46 | assertEquals(string + object, __); 47 | } 48 | 49 | @Koan 50 | public void toStringIsTestedForNullWhenInvokedImplicitly() { 51 | String string = "string"; 52 | assertEquals(string + null, __); 53 | } 54 | 55 | private Class[] getAncestors(Object object) { 56 | List> ancestors = new ArrayList>(); 57 | Class clazz = object.getClass(); 58 | while (clazz != null) { 59 | ancestors.add(clazz); 60 | clazz = clazz.getSuperclass(); 61 | } 62 | return ancestors.toArray(new Class[]{}); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutObjects.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.text.MessageFormat; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import static com.sandwich.koan.constant.KoanConstants.__; 10 | import static com.sandwich.util.Assert.assertEquals; 11 | 12 | public class AboutObjects { 13 | 14 | @Koan 15 | public void newObjectInstancesCanBeCreatedDirectly() { 16 | assertEquals(new Object() instanceof Object, __); 17 | } 18 | 19 | @Koan 20 | public void allClassesInheritFromObject() { 21 | class Foo { 22 | } 23 | 24 | Class[] ancestors = getAncestors(new Foo()); 25 | assertEquals(ancestors[0], __); 26 | assertEquals(ancestors[1], __); 27 | } 28 | 29 | @Koan 30 | public void objectToString() { 31 | Object object = new Object(); 32 | // TODO: Why is it best practice to ALWAYS override toString? 33 | String expectedToString = MessageFormat.format("{0}@{1}", Object.class.getName(), Integer.toHexString(object.hashCode())); 34 | assertEquals(expectedToString, __); // hint: object.toString() 35 | } 36 | 37 | @Koan 38 | public void toStringConcatenates() { 39 | final String string = "ha"; 40 | Object object = new Object() { 41 | @Override 42 | public String toString() { 43 | return string; 44 | } 45 | }; 46 | assertEquals(string + object, __); 47 | } 48 | 49 | @Koan 50 | public void toStringIsTestedForNullWhenInvokedImplicitly() { 51 | String string = "string"; 52 | assertEquals(string + null, __); 53 | } 54 | 55 | private Class[] getAncestors(Object object) { 56 | List> ancestors = new ArrayList>(); 57 | Class clazz = object.getClass(); 58 | while (clazz != null) { 59 | ancestors.add(clazz); 60 | clazz = clazz.getSuperclass(); 61 | } 62 | return ancestors.toArray(new Class[]{}); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/path/xmltransformation/RbVariableInjector.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.path.xmltransformation; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | import com.sandwich.koan.constant.KoanConstants; 6 | import com.sandwich.util.Strings; 7 | import com.sandwich.util.io.directories.DirectoryManager; 8 | import com.sandwich.util.io.filecompiler.FileCompiler; 9 | 10 | public class RbVariableInjector { 11 | 12 | private final String lesson; 13 | private final String methodName; 14 | private final String suiteName; 15 | private final String suitePath; 16 | 17 | 18 | public RbVariableInjector(String lesson, Method koanMethod){ 19 | if(koanMethod == null){ 20 | throw new IllegalArgumentException("koanMethod may not be null"); 21 | } 22 | Class declaringClass = koanMethod.getDeclaringClass(); 23 | if(lesson == null){ 24 | lesson = Strings.getMessage(declaringClass.getCanonicalName() + '.' + koanMethod.getName()); 25 | if(Strings.wasNotFound(lesson)){ 26 | lesson = null; 27 | } 28 | } 29 | this.lesson = lesson; 30 | methodName = koanMethod.getName(); 31 | suiteName = declaringClass.getSimpleName(); 32 | String path = FileCompiler.getSourceFileFromClass(DirectoryManager.getSourceDir(), declaringClass.getName()).getAbsolutePath(); 33 | suitePath = path.substring(0, path.lastIndexOf(DirectoryManager.FILESYSTEM_SEPARATOR) + 1); 34 | } 35 | 36 | public String injectLessonVariables() { 37 | if(lesson == null || lesson.length() == 0 || 38 | lesson.indexOf(KoanConstants.XML_PARAMETER_START) < 0){ 39 | return lesson; 40 | } 41 | StringBuffer sb = new StringBuffer(lesson); 42 | replace(sb, XmlVariableDictionary.FILE_NAME, suiteName); 43 | replace(sb, XmlVariableDictionary.FILE_PATH, suitePath); 44 | replace(sb, XmlVariableDictionary.METHOD_NAME, methodName); 45 | return sb.toString(); 46 | } 47 | 48 | private void replace(StringBuffer sb, String keyword, String value) { 49 | int indexOfKeyword = sb.indexOf(keyword); 50 | if(indexOfKeyword < 0){ 51 | return; 52 | } 53 | sb.replace(indexOfKeyword, indexOfKeyword+keyword.length(), value); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/Assert.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util; 2 | 3 | import com.sandwich.koan.KoanIncompleteException; 4 | import com.sandwich.koan.constant.KoanConstants; 5 | 6 | public class Assert { 7 | 8 | static final String EXPECTED = "expected:<"; 9 | static final String END = ">"; 10 | static final String BUT_WAS = "> but was:<"; 11 | 12 | public static void assertEquals(String msg, Object o0, Object o1){ 13 | if(o0 == null && o1 != null){ 14 | fail(msg, o0, o1); 15 | } 16 | if(o1 == null && o0 != null){ 17 | fail(msg, o0, o1); 18 | } 19 | // not if o0 == o1 return, because equals may violate contract (though 20 | // that's obviously strongly discouraged), but cannot invoke equals on 21 | // null pointer w/o sacrificing functionality from anticipating failure 22 | if(o1 == null && o0 == null){ 23 | return; 24 | } 25 | if(!o0.equals(o1)){ 26 | fail(msg, o0, o1); 27 | } 28 | } 29 | 30 | public static void assertEquals(Object o0, Object o1){ 31 | assertEquals("", o0, o1); 32 | } 33 | 34 | public static void assertTrue(Object t){ 35 | assertEquals(true,t); 36 | } 37 | 38 | public static void assertFalse(Object f){ 39 | assertEquals(false,f); 40 | } 41 | 42 | public static void assertNull(Object o){ 43 | assertEquals(null, o); 44 | } 45 | 46 | public static void assertNotNull(Object o){ 47 | if(o == null){ 48 | fail("something other than null",o); 49 | } 50 | } 51 | 52 | public static void assertSame(Object o0, Object o1){ 53 | if(o0 != o1){ 54 | fail("Are the same instance... ",o0,o1); 55 | } 56 | } 57 | 58 | public static void assertNotSame(Object o0, Object o1){ 59 | if(o0 == o1){ 60 | fail("Not the same instance... ",o0,o1); 61 | } 62 | } 63 | 64 | public static void fail(Object o0, Object o1) throws KoanIncompleteException { 65 | fail("", o0, o1); 66 | } 67 | 68 | public static void fail(String msg, Object o0, Object o1){ 69 | fail(msg+(msg.length() == 0 ? "" : KoanConstants.EOL)+EXPECTED+o0+BUT_WAS+o1+END); 70 | } 71 | 72 | public static void fail(String msg){ 73 | throw new KoanIncompleteException(msg); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/util/StringsTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.util.Locale; 8 | import java.util.logging.Level; 9 | import java.util.logging.LogRecord; 10 | 11 | import org.junit.Test; 12 | 13 | import com.sandwich.koan.LocaleSwitchingTestCase; 14 | 15 | public class StringsTest extends LocaleSwitchingTestCase { 16 | 17 | @Test 18 | public void testStringsBundleInitializationWithNonDefaultLocale() throws Exception { 19 | Locale.setDefault(Locale.CHINA); 20 | assertNotNull(Strings.createResourceBundle()); 21 | } 22 | 23 | @Test 24 | public void testFallsBackToEnglishXmlWhenNoXmlForLocaleIsFound_eventIsLogged(){ 25 | Locale.setDefault(Locale.CHINA); 26 | assertLogged(Strings.class.getName(), new RBSensitiveLoggerExpectation(){ 27 | @Override 28 | protected void logCalled(LogRecord record) { 29 | assertEquals(Level.INFO, record.getLevel()); 30 | String expectation0 = "Your default language is not supported yet. "; 31 | String msg = record.getMessage(); 32 | assertTrue(msg.startsWith(expectation0)); 33 | // c:\meh\always funny stuff %$#\messages_ze.properties or /User/nix machine/messages_ze.properties... 34 | assertTrue(msg.contains("messages_zh.properties")); 35 | } 36 | }); 37 | } 38 | 39 | @Test 40 | public void testEnglishXmlWhenXmlForLocaleIsFound_eventIsNotLogged(){ 41 | Locale.setDefault(Locale.US); 42 | assertLogged(Strings.class.getName(), new RBSensitiveLoggerExpectation(){ 43 | @Override 44 | protected boolean isLogCallRequired() { 45 | return false; 46 | } 47 | }); 48 | } 49 | 50 | @Test 51 | public void testStringsBundleInitializationWithDefaultLocale() throws Exception { 52 | Locale.setDefault(Locale.US); 53 | assertNotNull(Strings.createResourceBundle()); 54 | } 55 | 56 | private class RBSensitiveLoggerExpectation extends LoggerExpectation { 57 | @Override 58 | protected void invokeImplementation() { 59 | Strings.createResourceBundle(); 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/cmdline/CommandLineArgumentBuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.util.Map; 7 | import java.util.Map.Entry; 8 | 9 | import org.junit.Test; 10 | 11 | import com.sandwich.koan.constant.ArgumentType; 12 | import com.sandwich.util.SimpleEntry; 13 | 14 | 15 | public class CommandLineArgumentBuilderTest { 16 | 17 | @Test 18 | public void testNoArguments() throws Exception { 19 | assertEquals(ArgumentType.RUN_KOANS, new CommandLineArgumentBuilder().entrySet().iterator().next().getKey()); 20 | } 21 | 22 | @Test 23 | public void testMethodArg_constructedExplicitly() throws Exception { 24 | String value = "someMethodName"; 25 | Entry anticipatedResult = 26 | new SimpleEntry(ArgumentType.METHOD_ARG, 27 | new CommandLineArgument(ArgumentType.METHOD_ARG, value)); 28 | Map commandLineArgs = new CommandLineArgumentBuilder( 29 | ArgumentType.METHOD_ARG.args().iterator().next(), 30 | value); 31 | assertEquals(1, commandLineArgs.size()); 32 | assertEquals(anticipatedResult, commandLineArgs.entrySet().iterator().next()); 33 | } 34 | 35 | @Test 36 | public void testClassArg_constructedImplicitly() throws Exception { 37 | String value = Object.class.getName(); 38 | Map commandLineArgs = new CommandLineArgumentBuilder(value); 39 | assertEquals(2, commandLineArgs.size()); 40 | assertTrue(commandLineArgs.containsKey(ArgumentType.CLASS_ARG)); 41 | assertTrue(commandLineArgs.containsKey(ArgumentType.RUN_KOANS)); 42 | } 43 | 44 | @Test 45 | public void testClassArg_constructedExplicitly() throws Exception { 46 | String value = Object.class.getName(); 47 | Map commandLineArgs = new CommandLineArgumentBuilder( 48 | ArgumentType.CLASS_ARG.args().iterator().next(), value); 49 | assertEquals(2, commandLineArgs.size()); 50 | assertTrue(commandLineArgs.containsKey(ArgumentType.CLASS_ARG)); 51 | assertTrue(commandLineArgs.containsKey(ArgumentType.RUN_KOANS)); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/util/KoanComparatorTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util; 2 | 3 | import static org.junit.Assert.assertSame; 4 | 5 | import java.lang.reflect.Method; 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | import org.junit.Test; 11 | 12 | import com.sandwich.koan.Koan; 13 | import com.sandwich.koan.KoanMethod; 14 | import com.sandwich.koan.path.CommandLineTestCase; 15 | 16 | public class KoanComparatorTest extends CommandLineTestCase { 17 | 18 | interface Caps { 19 | public String capitalize(String name); 20 | } 21 | 22 | @Test 23 | public void testThatKomparatorBombsWhenNotFound() throws Exception { 24 | Method m = new Object(){ 25 | @Koan public void someMethod(){} 26 | }.getClass().getDeclaredMethod("someMethod"); 27 | KoanComparator comparator = new KoanComparator(); 28 | try{ 29 | comparator.compare(KoanMethod.getInstance("2",m), KoanMethod.getInstance("1",m)); 30 | }catch(RuntimeException fileNotFound){} 31 | } 32 | 33 | @Test 34 | public void testComparatorRanksByOrder() throws Exception { 35 | Class clazz = new Object(){ 36 | @Koan public void someMethodOne(){} 37 | @Koan public void someMethodTwo(){} 38 | }.getClass(); 39 | KoanMethod m1 = KoanMethod.getInstance("",clazz.getDeclaredMethod("someMethodOne")); 40 | KoanMethod m2 = KoanMethod.getInstance("",clazz.getDeclaredMethod("someMethodTwo")); 41 | List methods = Arrays.asList(m2,m1); 42 | Collections.sort(methods, new KoanComparator()); 43 | assertSame(m1,methods.get(0)); 44 | assertSame(m2,methods.get(1)); 45 | } 46 | 47 | @Test 48 | public void testVariableNamesArentConfusedAsKoanMethodsWhenSorting() throws Exception { 49 | Class clazz = new Object(){ 50 | String foo; 51 | @Koan public void bar(){} 52 | @Koan public void foo(){} 53 | }.getClass(); 54 | KoanMethod m1 = KoanMethod.getInstance("", clazz.getDeclaredMethod("bar")); 55 | KoanMethod m2 = KoanMethod.getInstance("", clazz.getDeclaredMethod("foo")); 56 | List methods = Arrays.asList(m2,m1); 57 | Collections.sort(methods, new KoanComparator()); 58 | assertSame(m1,methods.get(0)); 59 | assertSame(m2,methods.get(1)); 60 | } 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/runner/AppLauncherTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.runner; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import org.junit.Test; 12 | 13 | import com.sandwich.koan.cmdline.CommandLineArgument; 14 | import com.sandwich.koan.cmdline.CommandLineArgumentBuilder; 15 | import com.sandwich.koan.constant.ArgumentType; 16 | 17 | public class AppLauncherTest { 18 | 19 | @Test 20 | public void testNecessityOfAddingRunKoansCommandLineArgument_addsIfNoArgsPresent(){ //default target 21 | Map args = new CommandLineArgumentBuilder(); 22 | assertArgsContains(true, args, ArgumentType.RUN_KOANS); 23 | } 24 | 25 | @Test 26 | public void testNecessityOfAddingRunKoansCommandLineArgument_ifClassArgIsPresent(){ 27 | Map args = new CommandLineArgumentBuilder(Object.class.getName()); 28 | assertArgsContains(true, args, ArgumentType.RUN_KOANS, ArgumentType.CLASS_ARG); 29 | } 30 | 31 | @Test 32 | public void testNecessityOfAddingRunKoansCommandLineArgument_doesntIfClassArgIsntPresent(){ 33 | List types = new ArrayList(Arrays.asList(ArgumentType.values())); 34 | assertTrue(types.remove(ArgumentType.CLASS_ARG)); 35 | assertTrue(types.remove(ArgumentType.DEBUG)); 36 | assertTrue(types.remove(ArgumentType.RUN_KOANS)); 37 | for(ArgumentType type : types){ 38 | Map args = new CommandLineArgumentBuilder(type.args().iterator().next()); 39 | assertArgsContains(false, args, ArgumentType.RUN_KOANS); 40 | assertArgsContains(true, args, type); 41 | } 42 | } 43 | 44 | private static void assertArgsContains( 45 | boolean shouldContain, Map args, ArgumentType...types) { 46 | if(shouldContain){ 47 | assertEquals("expected arguments of a certain length, but found those built were of a differing size", 48 | types.length, args.size()); 49 | } 50 | for(ArgumentType type : types){ 51 | assertEquals("the arguments built should" 52 | + (shouldContain ? "" : "n't") 53 | + " contain the type: "+type, shouldContain, args.containsKey(type)); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/FileMonitorFactory.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | import java.util.LinkedHashMap; 5 | import java.util.Map; 6 | import java.util.Map.Entry; 7 | 8 | import com.sandwich.koan.ApplicationSettings; 9 | 10 | public class FileMonitorFactory { 11 | 12 | private static Map monitors = new LinkedHashMap(); 13 | public static final int SLEEP_TIME_IN_MS = 500; 14 | 15 | static { 16 | Runtime.getRuntime().addShutdownHook(new Thread(new Runnable(){ 17 | public void run() { 18 | for(FileMonitor monitor : monitors.values()){ 19 | monitor.close(); 20 | } 21 | monitors.clear(); 22 | } 23 | })); 24 | Thread pollingThread = new Thread(new Runnable(){ 25 | public void run() { 26 | do{ 27 | if(ApplicationSettings.isInteractive()){ 28 | try { 29 | Thread.sleep(SLEEP_TIME_IN_MS); 30 | } catch (InterruptedException e) { 31 | throw new RuntimeException(e); 32 | } 33 | } 34 | for(Entry filePathAndMonitor : monitors.entrySet()){ 35 | FileMonitor monitor = filePathAndMonitor.getValue(); 36 | if(monitor != null){ 37 | monitor.notifyListeners(); 38 | } 39 | } 40 | }while(ApplicationSettings.isInteractive()); 41 | } 42 | }); 43 | pollingThread.setName("FileMonitorPolling"); 44 | if (ApplicationSettings.isInteractive()) { 45 | pollingThread.start(); 46 | } 47 | } 48 | 49 | public static FileMonitor getInstance(File monitoredFile, File dataFile) { 50 | String key = monitoredFile.getAbsolutePath() + dataFile.getAbsolutePath(); 51 | FileMonitor monitor = monitors.get(key); 52 | if(monitor == null){ 53 | monitor = new FileMonitor(monitoredFile, dataFile); 54 | monitors.put(key, monitor); 55 | } 56 | return monitor; 57 | } 58 | 59 | public static void removeInstance(FileMonitor monitor){ 60 | monitor.close(); 61 | monitors.remove(monitor.getFilesystemPath()); 62 | } 63 | 64 | public static void removeInstance(String absolutePath) { 65 | monitors.remove(absolutePath); 66 | } 67 | 68 | public static void closeAll() { 69 | for(FileMonitor monitor : monitors.values()){ 70 | monitor.close(); 71 | } 72 | monitors.clear(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgument.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline; 2 | 3 | import java.util.Arrays; 4 | 5 | import com.sandwich.koan.constant.ArgumentType; 6 | 7 | public class CommandLineArgument implements Runnable, Comparable { 8 | private ArgumentType argType; 9 | private String[] values; 10 | boolean isPlantedByApp = false; 11 | public CommandLineArgument(ArgumentType argType, String... values) { 12 | this(argType, false, values); 13 | } 14 | 15 | public CommandLineArgument(ArgumentType argType, boolean isPlantedByApp, String... values) { 16 | this.argType = argType; 17 | this.values = values; 18 | this.isPlantedByApp = isPlantedByApp; 19 | } 20 | 21 | public String[] getValues() { 22 | return values; 23 | } 24 | 25 | public ArgumentType getArgumentType() { 26 | return argType; 27 | } 28 | 29 | public void setPlantedByApp(boolean isPlantedByApp){ 30 | this.isPlantedByApp = isPlantedByApp; 31 | } 32 | 33 | public boolean isPlantedByApp() { 34 | return isPlantedByApp; 35 | } 36 | 37 | public void run(){ 38 | argType.run(values); 39 | } 40 | 41 | public int compareTo(CommandLineArgument o) { 42 | if(o == null){ 43 | return -1; 44 | } 45 | if(argType == null){ 46 | return 1; 47 | } 48 | return argType.compareTo(o.getArgumentType()); 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return new StringBuilder("CommandLineArgument [argType=").append(argType) 54 | .append(", value=").append(Arrays.toString(values)).append("]").toString(); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | final int prime = 31; 60 | int result = 1; 61 | result = prime * result + ((argType == null) ? 0 : argType.hashCode()); 62 | result = prime * result + ((values == null) ? 0 : Arrays.hashCode(values)); 63 | return result; 64 | } 65 | 66 | @Override 67 | public boolean equals(Object obj) { 68 | if (this == obj) 69 | return true; 70 | if (obj == null) 71 | return false; 72 | if (getClass() != obj.getClass()) 73 | return false; 74 | CommandLineArgument other = (CommandLineArgument) obj; 75 | if (argType != other.argType) 76 | return false; 77 | if (values == null) { 78 | if (other.values != null) 79 | return false; 80 | } else if (!Arrays.equals(values, other.values)) 81 | return false; 82 | return true; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/path/RbVariableInjectorTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.path; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertSame; 5 | import static org.junit.Assert.assertTrue; 6 | import static org.junit.Assert.fail; 7 | 8 | import org.junit.Test; 9 | 10 | import com.sandwich.koan.path.xmltransformation.RbVariableInjector; 11 | import com.sandwich.koan.suite.OneFailingKoan; 12 | 13 | public class RbVariableInjectorTest extends CommandLineTestCase { 14 | 15 | @Test 16 | public void construction_nullMethod() throws Exception { 17 | try{ 18 | new RbVariableInjector("", null); 19 | fail("why construct w/ null method?"); 20 | }catch(IllegalArgumentException t){ 21 | // this is ok - we want this! 22 | } 23 | } 24 | 25 | @Test 26 | public void injectInputVariables_filePath() throws Exception { 27 | String lesson = "meh ${file_path}"; 28 | String result = new RbVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) 29 | .injectLessonVariables(); 30 | String firstPkgName = "com"; 31 | // just inspect anything beyond the root of the project 32 | result = result.substring(result.indexOf(firstPkgName)+firstPkgName.length(), result.length()); 33 | assertTrue(result.indexOf(firstPkgName) < result.indexOf("sandwich")); 34 | assertTrue(result.indexOf("sandwich") < result.indexOf("koan")); 35 | assertTrue(result.indexOf("koan") < result.indexOf("suite")); 36 | } 37 | 38 | @Test 39 | public void injectInputVariables_fileName() throws Exception { 40 | String lesson = "meh ${file_name}"; 41 | String result = new RbVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) 42 | .injectLessonVariables(); 43 | assertEquals("meh OneFailingKoan", result); 44 | } 45 | 46 | @Test 47 | public void injectInputVariables_methodName() throws Exception { 48 | String lesson = "meh ${method_name}"; 49 | String result = new RbVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) 50 | .injectLessonVariables(); 51 | assertEquals("meh koanMethod", result); 52 | } 53 | 54 | @Test 55 | public void nothingToInject() throws Exception { 56 | String lesson = " meh ea asdwdw s "; 57 | assertSame(lesson, new RbVariableInjector(lesson, 58 | OneFailingKoan.class.getDeclaredMethod("koanMethod")).injectLessonVariables()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/ApplicationSettings.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan; 2 | 3 | import java.io.FileInputStream; 4 | import java.util.PropertyResourceBundle; 5 | import java.util.ResourceBundle; 6 | 7 | import com.sandwich.util.io.directories.DirectoryManager; 8 | 9 | public class ApplicationSettings { 10 | 11 | private static ResourceBundle CONFIG_BUNDLE; 12 | 13 | private static boolean debug; 14 | 15 | public static void setDebug(boolean debug2) { 16 | debug = debug2; 17 | } 18 | 19 | public static boolean isDebug(){ 20 | return debug || isEqual(getConfigBundle().getString("debug"), true, true); 21 | } 22 | 23 | public static boolean isEncouragementEnabled(){ 24 | return isEqual(getConfigBundle().getString("enable_encouragement"), true, true); 25 | } 26 | 27 | public static boolean isExpectationResultVisible(){ 28 | return isEqual(getConfigBundle().getString("enable_expectation_result"), true, true); 29 | } 30 | 31 | public static boolean isInteractive(){ 32 | return isEqual(getConfigBundle().getString("interactive"), true, true); 33 | } 34 | 35 | public static char getExitChar(){ 36 | return getConfigBundle().getString("exit_character").charAt(0); 37 | } 38 | 39 | public static String getPathXmlFileName(){ 40 | return getConfigBundle().getString("path_xml_filename"); 41 | } 42 | 43 | public static long getFileCompilationTimeoutInMs(){ 44 | return Long.valueOf(getConfigBundle().getString("compile_timeout_in_ms")); 45 | } 46 | 47 | public static String getMonitorIgnorePattern(){ 48 | return getConfigBundle().getString("ignore_from_monitoring"); 49 | } 50 | 51 | private static boolean isEqual(String value, Object e2, boolean ignoreCase){ 52 | if(value == e2){ 53 | return true; 54 | } 55 | if(value != null){ 56 | String stringValue = String.valueOf(e2); 57 | return ignoreCase ? value.equalsIgnoreCase(stringValue) : value.equals(stringValue); 58 | } 59 | return false; 60 | } 61 | 62 | private static ResourceBundle getConfigBundle(){ 63 | if(CONFIG_BUNDLE == null){ 64 | try { 65 | CONFIG_BUNDLE = new PropertyResourceBundle(new FileInputStream( 66 | DirectoryManager.injectFileSystemSeparators( 67 | DirectoryManager.getConfigDir(), "config.properties"))); 68 | } catch (Exception e) { 69 | throw new RuntimeException(e); 70 | } 71 | } 72 | return CONFIG_BUNDLE; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /koans/src/intermediate/AboutDates.java: -------------------------------------------------------------------------------- 1 | package intermediate; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.text.DateFormat; 6 | import java.text.ParseException; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Calendar; 9 | import java.util.Date; 10 | 11 | import static com.sandwich.koan.constant.KoanConstants.__; 12 | import static com.sandwich.util.Assert.assertEquals; 13 | 14 | 15 | public class AboutDates { 16 | 17 | private Date date = new Date(100010001000L); 18 | 19 | @Koan 20 | public void dateToString() { 21 | assertEquals(date.toString(), __); 22 | } 23 | 24 | @Koan 25 | public void changingDateValue() { 26 | int oneHourInMiliseconds = 3600000; 27 | date.setTime(date.getTime() + oneHourInMiliseconds); 28 | assertEquals(date.toString(), __); 29 | } 30 | 31 | @Koan 32 | public void usingCalendarToChangeDates() { 33 | Calendar cal = Calendar.getInstance(); 34 | cal.setTime(date); 35 | cal.add(Calendar.MONTH, 1); 36 | assertEquals(cal.getTime().toString(), __); 37 | } 38 | 39 | @Koan 40 | public void usingRollToChangeDatesDoesntWrapOtherFields() { 41 | Calendar cal = Calendar.getInstance(); 42 | cal.setTime(date); 43 | cal.roll(Calendar.MONTH, 12); 44 | assertEquals(cal.getTime().toString(), __); 45 | } 46 | 47 | @Koan 48 | public void usingDateFormatToFormatDate() { 49 | String formattedDate = DateFormat.getDateInstance().format(date); 50 | assertEquals(formattedDate, __); 51 | } 52 | 53 | @Koan 54 | public void usingDateFormatToFormatDateShort() { 55 | String formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(date); 56 | assertEquals(formattedDate, __); 57 | } 58 | 59 | @Koan 60 | public void usingDateFormatToFormatDateFull() { 61 | String formattedDate = DateFormat.getDateInstance(DateFormat.FULL).format(date); 62 | // There is also DateFormat.MEDIUM and DateFormat.LONG... you get the idea ;-) 63 | assertEquals(formattedDate, __); 64 | } 65 | 66 | @Koan 67 | public void usingDateFormatToParseDates() throws ParseException { 68 | DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy"); 69 | Date date2 = dateFormat.parse("01-01-2000"); 70 | assertEquals(date2.toString(), __); 71 | // What happened to the time? What do you need to change to keep the time as well? 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /koans/app/data/src/intermediate/AboutDates.java: -------------------------------------------------------------------------------- 1 | package intermediate; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.text.DateFormat; 6 | import java.text.ParseException; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Calendar; 9 | import java.util.Date; 10 | 11 | import static com.sandwich.koan.constant.KoanConstants.__; 12 | import static com.sandwich.util.Assert.assertEquals; 13 | 14 | 15 | public class AboutDates { 16 | 17 | private Date date = new Date(100010001000L); 18 | 19 | @Koan 20 | public void dateToString() { 21 | assertEquals(date.toString(), __); 22 | } 23 | 24 | @Koan 25 | public void changingDateValue() { 26 | int oneHourInMiliseconds = 3600000; 27 | date.setTime(date.getTime() + oneHourInMiliseconds); 28 | assertEquals(date.toString(), __); 29 | } 30 | 31 | @Koan 32 | public void usingCalendarToChangeDates() { 33 | Calendar cal = Calendar.getInstance(); 34 | cal.setTime(date); 35 | cal.add(Calendar.MONTH, 1); 36 | assertEquals(cal.getTime().toString(), __); 37 | } 38 | 39 | @Koan 40 | public void usingRollToChangeDatesDoesntWrapOtherFields() { 41 | Calendar cal = Calendar.getInstance(); 42 | cal.setTime(date); 43 | cal.roll(Calendar.MONTH, 12); 44 | assertEquals(cal.getTime().toString(), __); 45 | } 46 | 47 | @Koan 48 | public void usingDateFormatToFormatDate() { 49 | String formattedDate = DateFormat.getDateInstance().format(date); 50 | assertEquals(formattedDate, __); 51 | } 52 | 53 | @Koan 54 | public void usingDateFormatToFormatDateShort() { 55 | String formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(date); 56 | assertEquals(formattedDate, __); 57 | } 58 | 59 | @Koan 60 | public void usingDateFormatToFormatDateFull() { 61 | String formattedDate = DateFormat.getDateInstance(DateFormat.FULL).format(date); 62 | // There is also DateFormat.MEDIUM and DateFormat.LONG... you get the idea ;-) 63 | assertEquals(formattedDate, __); 64 | } 65 | 66 | @Koan 67 | public void usingDateFormatToParseDates() throws ParseException { 68 | DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy"); 69 | Date date2 = dateFormat.parse("01-01-2000"); 70 | assertEquals(date2.toString(), __); 71 | // What happened to the time? What do you need to change to keep the time as well? 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /koans/src/intermediate/AboutComparison.java: -------------------------------------------------------------------------------- 1 | package intermediate; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.Arrays; 6 | import java.util.Comparator; 7 | 8 | import static com.sandwich.koan.constant.KoanConstants.__; 9 | import static com.sandwich.util.Assert.assertEquals; 10 | 11 | public class AboutComparison { 12 | 13 | @Koan 14 | public void compareObjects() { 15 | String a = "abc"; 16 | String b = "bcd"; 17 | assertEquals(a.compareTo(b), __); 18 | assertEquals(a.compareTo(a), __); 19 | assertEquals(b.compareTo(a), __); 20 | } 21 | 22 | static class Car implements Comparable { 23 | int horsepower; 24 | 25 | // For an explanation for this implementation look at 26 | // http://download.oracle.com/javase/6/docs/api/java/lang/Comparable.html#compareTo(T) 27 | public int compareTo(Car o) { 28 | return horsepower - o.horsepower; 29 | } 30 | 31 | } 32 | 33 | @Koan 34 | public void makeObjectsComparable() { 35 | Car vwbeetle = new Car(); 36 | vwbeetle.horsepower = 50; 37 | Car porsche = new Car(); 38 | porsche.horsepower = 300; 39 | assertEquals(vwbeetle.compareTo(porsche), __); 40 | } 41 | 42 | static class RaceHorse { 43 | int speed; 44 | int age; 45 | 46 | @Override 47 | public String toString() { 48 | return "Speed: " + speed + " Age: " + age; 49 | } 50 | } 51 | 52 | static class HorseSpeedComparator implements Comparator { 53 | public int compare(RaceHorse o1, RaceHorse o2) { 54 | return o1.speed - o2.speed; 55 | } 56 | } 57 | 58 | static class HorseAgeComparator implements Comparator { 59 | public int compare(RaceHorse o1, RaceHorse o2) { 60 | return o1.age - o2.age; 61 | } 62 | } 63 | 64 | @Koan 65 | public void makeObjectsComparableWithoutComparable() { 66 | RaceHorse lindy = new RaceHorse(); 67 | lindy.age = 10; 68 | lindy.speed = 2; 69 | RaceHorse lightning = new RaceHorse(); 70 | lightning.age = 2; 71 | lightning.speed = 10; 72 | RaceHorse slowy = new RaceHorse(); 73 | slowy.age = 12; 74 | slowy.speed = 1; 75 | 76 | RaceHorse[] horses = {lindy, slowy, lightning}; 77 | 78 | Arrays.sort(horses, new HorseAgeComparator()); 79 | assertEquals(horses[0], __); 80 | Arrays.sort(horses, new HorseSpeedComparator()); 81 | assertEquals(horses[0], __); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /koans/app/data/src/intermediate/AboutComparison.java: -------------------------------------------------------------------------------- 1 | package intermediate; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.Arrays; 6 | import java.util.Comparator; 7 | 8 | import static com.sandwich.koan.constant.KoanConstants.__; 9 | import static com.sandwich.util.Assert.assertEquals; 10 | 11 | public class AboutComparison { 12 | 13 | @Koan 14 | public void compareObjects() { 15 | String a = "abc"; 16 | String b = "bcd"; 17 | assertEquals(a.compareTo(b), __); 18 | assertEquals(a.compareTo(a), __); 19 | assertEquals(b.compareTo(a), __); 20 | } 21 | 22 | static class Car implements Comparable { 23 | int horsepower; 24 | 25 | // For an explanation for this implementation look at 26 | // http://download.oracle.com/javase/6/docs/api/java/lang/Comparable.html#compareTo(T) 27 | public int compareTo(Car o) { 28 | return horsepower - o.horsepower; 29 | } 30 | 31 | } 32 | 33 | @Koan 34 | public void makeObjectsComparable() { 35 | Car vwbeetle = new Car(); 36 | vwbeetle.horsepower = 50; 37 | Car porsche = new Car(); 38 | porsche.horsepower = 300; 39 | assertEquals(vwbeetle.compareTo(porsche), __); 40 | } 41 | 42 | static class RaceHorse { 43 | int speed; 44 | int age; 45 | 46 | @Override 47 | public String toString() { 48 | return "Speed: " + speed + " Age: " + age; 49 | } 50 | } 51 | 52 | static class HorseSpeedComparator implements Comparator { 53 | public int compare(RaceHorse o1, RaceHorse o2) { 54 | return o1.speed - o2.speed; 55 | } 56 | } 57 | 58 | static class HorseAgeComparator implements Comparator { 59 | public int compare(RaceHorse o1, RaceHorse o2) { 60 | return o1.age - o2.age; 61 | } 62 | } 63 | 64 | @Koan 65 | public void makeObjectsComparableWithoutComparable() { 66 | RaceHorse lindy = new RaceHorse(); 67 | lindy.age = 10; 68 | lindy.speed = 2; 69 | RaceHorse lightning = new RaceHorse(); 70 | lightning.age = 2; 71 | lightning.speed = 10; 72 | RaceHorse slowy = new RaceHorse(); 73 | slowy.age = 12; 74 | slowy.speed = 1; 75 | 76 | RaceHorse[] horses = {lindy, slowy, lightning}; 77 | 78 | Arrays.sort(horses, new HorseAgeComparator()); 79 | assertEquals(horses[0], __); 80 | Arrays.sort(horses, new HorseSpeedComparator()); 81 | assertEquals(horses[0], __); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/KoanFileCompileAndRunListener.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.Collections; 6 | import java.util.Map; 7 | 8 | import com.sandwich.koan.ApplicationSettings; 9 | import com.sandwich.koan.cmdline.CommandLineArgument; 10 | import com.sandwich.koan.cmdline.CommandLineArgumentRunner; 11 | import com.sandwich.koan.constant.ArgumentType; 12 | import com.sandwich.koan.constant.KoanConstants; 13 | import com.sandwich.koan.util.ApplicationUtils; 14 | import com.sandwich.util.io.classloader.DynamicClassLoader; 15 | import com.sandwich.util.io.directories.DirectoryManager; 16 | import com.sandwich.util.io.filecompiler.CompilerConfig; 17 | import com.sandwich.util.io.filecompiler.FileCompiler; 18 | 19 | public class KoanFileCompileAndRunListener implements FileListener { 20 | 21 | private Map args = Collections.emptyMap(); 22 | private KoanSuiteCompilationListener listener = new KoanSuiteCompilationListener(); 23 | 24 | public KoanFileCompileAndRunListener(Map args) throws IOException{ 25 | this.args = args; 26 | } 27 | 28 | public void fileSaved(File file) { 29 | String absolutePath = file.getAbsolutePath(); 30 | if(CompilerConfig.isSourceFile(absolutePath)){ 31 | ApplicationUtils.getPresenter().displayMessage(KoanConstants.EOL+"loading: "+absolutePath); 32 | File[] jars = new File(DirectoryManager.getProjectLibraryDir()).listFiles(); 33 | String[] classPath = new String[jars.length]; 34 | for (int i = 0; i < jars.length; i++) { 35 | String jarPath = jars[i].getAbsolutePath(); 36 | String jarPathLowerCase = jarPath.toLowerCase(); 37 | if(jarPathLowerCase.endsWith(".jar") || jarPathLowerCase.endsWith(".war")){ 38 | classPath[i] = jarPath; 39 | } 40 | } 41 | try { 42 | FileCompiler.compile(file, 43 | new File(DirectoryManager.getBinDir()), 44 | listener, 45 | ApplicationSettings.getFileCompilationTimeoutInMs(), 46 | classPath); 47 | DynamicClassLoader.remove(FileCompiler.sourceToClass( 48 | DirectoryManager.getSourceDir(), DirectoryManager.getBinDir(), file).toURI().toURL()); 49 | if(!listener.isLastCompilationAttemptFailure()){ 50 | ApplicationUtils.getPresenter().clearMessages(); 51 | new CommandLineArgumentRunner(args).run(); 52 | } 53 | } catch (Exception e) { 54 | throw new RuntimeException(e); 55 | } 56 | } 57 | } 58 | 59 | public void newFile(File file) { 60 | 61 | } 62 | 63 | public void fileDeleted(File file) { 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/util/io/DirectoryManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import com.sandwich.koan.path.CommandLineTestCase; 8 | import com.sandwich.util.io.directories.DirectoryManager; 9 | 10 | 11 | public class DirectoryManagerTest extends CommandLineTestCase { 12 | 13 | private static final String sep = System.getProperty("file.separator"); 14 | private static final String start = 15 | System.getProperty("os.name").toLowerCase().contains("windows") ? 16 | "" : sep; 17 | 18 | @Test 19 | public void testFileSeparatorInjection_happyPath() throws Exception { 20 | assertEquals(start+"home"+sep+"wilford"+sep+"liberty", DirectoryManager.injectFileSystemSeparators( 21 | "home", "wilford", "liberty")); 22 | } 23 | 24 | @Test 25 | public void testFileSeparatorInjection_separatorsAtBeginning() throws Exception { 26 | assertEquals(start+"home"+sep+"wilford"+sep+"liberty", DirectoryManager.injectFileSystemSeparators( 27 | sep+"home", sep+"wilford", sep+"liberty")); 28 | } 29 | 30 | @Test 31 | public void testFileSeparatorInjection_separatorsAtEnding() throws Exception { 32 | assertEquals(start+"home"+sep+"wilford"+sep+"liberty", DirectoryManager.injectFileSystemSeparators( 33 | "home"+sep, "wilford"+sep, "liberty"+sep)); 34 | } 35 | 36 | @Test 37 | public void testFileSeparatorInjection_separatorsAtBeginningAndEnding() throws Exception { 38 | assertEquals(start+"home"+sep+"wilford"+sep+"liberty", DirectoryManager.injectFileSystemSeparators( 39 | "home"+sep, "wilford"+sep, sep+"liberty"+sep)); 40 | } 41 | 42 | @Test 43 | public void testFileSeparatorInjection_separatorsInMiddle() throws Exception { 44 | assertEquals(start+"home"+sep+"wilford"+sep+"liberty", DirectoryManager.injectFileSystemSeparators( 45 | "home", "wilford"+sep+"liberty")); 46 | } 47 | 48 | @Test 49 | public void testWindowsFileSystemPathWithSpaces() throws Exception { 50 | String osName = System.getProperty("os.name"); 51 | try{ 52 | System.setProperty("os.name", "Windows ME"); 53 | assertEquals("Documents and Settings", DirectoryManager.injectFileSystemSeparators("Documents%20and%20Settings")); 54 | }finally{ 55 | System.setProperty("os.name", osName); 56 | } 57 | } 58 | 59 | @Test 60 | public void testNonWindowsFileSystemPathWithSpaces() throws Exception { 61 | String osName = System.getProperty("os.name"); 62 | try{ 63 | System.setProperty("os.name", "Not MS"); 64 | assertEquals(sep+"Documents%20and%20Settings", DirectoryManager.injectFileSystemSeparators("Documents%20and%20Settings")); 65 | }finally{ 66 | System.setProperty("os.name", osName); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /koans/src/java8/AboutLambdas.java: -------------------------------------------------------------------------------- 1 | package java8; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.function.Function; 6 | 7 | import static com.sandwich.koan.constant.KoanConstants.__; 8 | import static com.sandwich.util.Assert.assertEquals; 9 | 10 | public class AboutLambdas { 11 | 12 | interface Caps { 13 | public String capitalize(String name); 14 | } 15 | 16 | String fieldFoo = "Lambdas"; 17 | 18 | @Override 19 | public String toString() { 20 | return "CAPS"; 21 | } 22 | 23 | static String str = ""; 24 | 25 | //lambda has access to "this" 26 | Caps thisLambdaField = s -> this.toString(); 27 | //lambda has access to object methods 28 | Caps toStringLambdaField = s -> toString(); 29 | 30 | @Koan 31 | public void verySimpleLambda() throws InterruptedException { 32 | Runnable r8 = () -> str = "changed in lambda"; 33 | r8.run(); 34 | assertEquals(str, __); 35 | } 36 | 37 | @Koan 38 | public void simpleLambda() { 39 | Caps caps = (String n) -> { 40 | return n.toUpperCase(); 41 | }; 42 | String capitalized = caps.capitalize("James"); 43 | assertEquals(capitalized, __); 44 | } 45 | 46 | @Koan 47 | public void simpleSuccinctLambda() { 48 | //parameter type can be omitted, 49 | //code block braces {} and return statement can be omitted for single statement lambda 50 | //parameter parenthesis can be omitted for single parameter lambda 51 | Caps caps = s -> s.toUpperCase(); 52 | String capitalized = caps.capitalize("Arthur"); 53 | assertEquals(capitalized, __); 54 | } 55 | 56 | @Koan 57 | public void lambdaField() { 58 | assertEquals(thisLambdaField.capitalize(""), __); 59 | } 60 | 61 | @Koan 62 | public void lambdaField2() { 63 | assertEquals(toStringLambdaField.capitalize(""), __); 64 | } 65 | 66 | @Koan 67 | public void effectivelyFinal() { 68 | //final can be omitted like this: 69 | /* final */ 70 | String effectivelyFinal = "I'm effectively final"; 71 | Caps caps = s -> effectivelyFinal.toUpperCase(); 72 | assertEquals(caps.capitalize(effectivelyFinal), __); 73 | } 74 | 75 | @Koan 76 | public void methodReference() { 77 | Caps caps = String::toUpperCase; 78 | String capitalized = caps.capitalize("Gosling"); 79 | assertEquals(capitalized, __); 80 | } 81 | 82 | @Koan 83 | public void thisIsSurroundingClass() { 84 | //"this" in lambda points to surrounding class 85 | Function foo = s -> s + this.fieldFoo + s; 86 | assertEquals(foo.apply("|"), __); 87 | } 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /koans/app/data/src/java8/AboutLambdas.java: -------------------------------------------------------------------------------- 1 | package java8; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.function.Function; 6 | 7 | import static com.sandwich.koan.constant.KoanConstants.__; 8 | import static com.sandwich.util.Assert.assertEquals; 9 | 10 | public class AboutLambdas { 11 | 12 | interface Caps { 13 | public String capitalize(String name); 14 | } 15 | 16 | String fieldFoo = "Lambdas"; 17 | 18 | @Override 19 | public String toString() { 20 | return "CAPS"; 21 | } 22 | 23 | static String str = ""; 24 | 25 | //lambda has access to "this" 26 | Caps thisLambdaField = s -> this.toString(); 27 | //lambda has access to object methods 28 | Caps toStringLambdaField = s -> toString(); 29 | 30 | @Koan 31 | public void verySimpleLambda() throws InterruptedException { 32 | Runnable r8 = () -> str = "changed in lambda"; 33 | r8.run(); 34 | assertEquals(str, __); 35 | } 36 | 37 | @Koan 38 | public void simpleLambda() { 39 | Caps caps = (String n) -> { 40 | return n.toUpperCase(); 41 | }; 42 | String capitalized = caps.capitalize("James"); 43 | assertEquals(capitalized, __); 44 | } 45 | 46 | @Koan 47 | public void simpleSuccinctLambda() { 48 | //parameter type can be omitted, 49 | //code block braces {} and return statement can be omitted for single statement lambda 50 | //parameter parenthesis can be omitted for single parameter lambda 51 | Caps caps = s -> s.toUpperCase(); 52 | String capitalized = caps.capitalize("Arthur"); 53 | assertEquals(capitalized, __); 54 | } 55 | 56 | @Koan 57 | public void lambdaField() { 58 | assertEquals(thisLambdaField.capitalize(""), __); 59 | } 60 | 61 | @Koan 62 | public void lambdaField2() { 63 | assertEquals(toStringLambdaField.capitalize(""), __); 64 | } 65 | 66 | @Koan 67 | public void effectivelyFinal() { 68 | //final can be omitted like this: 69 | /* final */ 70 | String effectivelyFinal = "I'm effectively final"; 71 | Caps caps = s -> effectivelyFinal.toUpperCase(); 72 | assertEquals(caps.capitalize(effectivelyFinal), __); 73 | } 74 | 75 | @Koan 76 | public void methodReference() { 77 | Caps caps = String::toUpperCase; 78 | String capitalized = caps.capitalize("Gosling"); 79 | assertEquals(capitalized, __); 80 | } 81 | 82 | @Koan 83 | public void thisIsSurroundingClass() { 84 | //"this" in lambda points to surrounding class 85 | Function foo = s -> s + this.fieldFoo + s; 86 | assertEquals(foo.apply("|"), __); 87 | } 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /koans/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/KoanClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan; 2 | 3 | import java.io.File; 4 | import java.io.UTFDataFormatException; 5 | 6 | import com.sandwich.koan.util.ApplicationUtils; 7 | import com.sandwich.util.io.FileMonitor; 8 | import com.sandwich.util.io.FileMonitorFactory; 9 | import com.sandwich.util.io.classloader.DynamicClassLoader; 10 | import com.sandwich.util.io.directories.DirectoryManager; 11 | 12 | public class KoanClassLoader extends DynamicClassLoader { 13 | 14 | private static KoanClassLoader instance; 15 | private final FileMonitor fileMonitor; 16 | 17 | private KoanClassLoader(){ 18 | this(DirectoryManager.getBinDir(), 19 | DirectoryManager.getSourceDir(), 20 | buildClassPath(), 21 | DynamicClassLoader.class.getClassLoader(), 22 | ApplicationSettings.getFileCompilationTimeoutInMs(), 23 | getFileMonitor()); 24 | } 25 | 26 | private static FileMonitor getFileMonitor() { 27 | try{ 28 | return FileMonitorFactory.getInstance(new File(DirectoryManager.getMainDir()), new File(DirectoryManager.getDataFile())); 29 | }catch(RuntimeException x){ 30 | if(x.getCause() instanceof UTFDataFormatException){ 31 | ApplicationUtils.getPresenter().displayError("Issue loading file system hashes, please rerun run.bat or run.sh with the -clear switch to reset."); 32 | System.exit(4); 33 | } 34 | throw x; 35 | } 36 | } 37 | 38 | private KoanClassLoader(String binDir, String sourceDir, 39 | String[] classPath, ClassLoader parent, 40 | long timeout, FileMonitor fileMonitor) { 41 | super(binDir, sourceDir, classPath, parent, timeout); 42 | this.fileMonitor = fileMonitor; 43 | } 44 | 45 | @Override 46 | public boolean isFileModifiedSinceLastPoll(String sourcePath, long lastModified) { 47 | return fileMonitor.isFileModifiedSinceLastPoll(sourcePath, lastModified); 48 | } 49 | 50 | @Override 51 | public void updateFileSavedTime(File sourceFile) { 52 | fileMonitor.updateFileSaveTime(sourceFile); 53 | } 54 | 55 | private static String[] buildClassPath() { 56 | File[] jars = new File(DirectoryManager.getProjectLibraryDir()).listFiles(); 57 | String[] classPath = new String[jars.length]; 58 | for (int i = 0; i < jars.length; i++) { 59 | if(jars[i].getAbsolutePath().toLowerCase().endsWith(".jar")){ 60 | classPath[i] = jars[i].getAbsolutePath(); 61 | } 62 | } 63 | return classPath; 64 | } 65 | 66 | synchronized public static DynamicClassLoader getInstance(){ 67 | if(instance == null){ 68 | instance = new KoanClassLoader(); 69 | } 70 | return instance.clone(); 71 | } 72 | 73 | @Override 74 | public KoanClassLoader clone(){ 75 | return new KoanClassLoader(getBinDir(), getSourceDir(), getClassPath(), getClass().getClassLoader(), getTimeout(), fileMonitor); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutArrays.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.Arrays; 6 | 7 | import static com.sandwich.koan.constant.KoanConstants.__; 8 | import static com.sandwich.util.Assert.assertEquals; 9 | 10 | public class AboutArrays { 11 | 12 | @Koan 13 | public void arraysDoNotConsiderElementsWhenEvaluatingEquality() { 14 | // arrays utilize default object equality (A == {1} B == {1}, though A 15 | // and B contain the same thing, the container is not the same 16 | // referenced array instance... 17 | assertEquals(new int[]{1}.equals(new int[]{1}), __); 18 | } 19 | 20 | @Koan 21 | public void cloneEqualityIsNotRespected() { //! 22 | int[] original = new int[]{1}; 23 | assertEquals(original.equals(original.clone()), __); 24 | } 25 | 26 | @Koan 27 | public void anArraysHashCodeMethodDoesNotConsiderElements() { 28 | int[] array0 = new int[]{0}; 29 | int[] array1 = new int[]{0}; 30 | assertEquals(Integer.valueOf(array0.hashCode()).equals(array1.hashCode()), __); // not equal! 31 | // TODO: ponder the consequences when arrays are used in Hash Collection implementations. 32 | } 33 | 34 | @Koan 35 | public void arraysHelperClassEqualsMethodConsidersElementsWhenDeterminingEquality() { 36 | int[] array0 = new int[]{0}; 37 | int[] array1 = new int[]{0}; 38 | assertEquals(Arrays.equals(array0, array1), __); // whew - what most people assume 39 | // about equals in regard to arrays! (logical equality) 40 | } 41 | 42 | @Koan 43 | public void arraysHelperClassHashCodeMethodConsidersElementsWhenDeterminingHashCode() { 44 | int[] array0 = new int[]{0}; 45 | int[] array1 = new int[]{0}; 46 | // whew - what most people assume about hashCode in regard to arrays! 47 | assertEquals(Integer.valueOf(Arrays.hashCode(array0)).equals(Arrays.hashCode(array1)), __); 48 | } 49 | 50 | @Koan 51 | public void arraysAreMutable() { 52 | final boolean[] oneBoolean = new boolean[]{false}; 53 | oneBoolean[0] = true; 54 | assertEquals(oneBoolean[0], __); 55 | } 56 | 57 | @Koan 58 | public void arraysAreIndexedAtZero() { 59 | int[] integers = new int[]{1, 2}; 60 | assertEquals(integers[0], __); 61 | assertEquals(integers[1], __); 62 | } 63 | 64 | @Koan 65 | public void arrayIndexOutOfBounds() { 66 | int[] array = new int[]{1}; 67 | @SuppressWarnings("unused") 68 | int meh = array[1]; // remember 0 based indexes, 1 is the 2nd element (which doesn't exist) 69 | } 70 | 71 | @Koan 72 | public void arrayLengthCanBeChecked() { 73 | assertEquals(new int[1].length, __); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutArrays.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.util.Arrays; 6 | 7 | import static com.sandwich.koan.constant.KoanConstants.__; 8 | import static com.sandwich.util.Assert.assertEquals; 9 | 10 | public class AboutArrays { 11 | 12 | @Koan 13 | public void arraysDoNotConsiderElementsWhenEvaluatingEquality() { 14 | // arrays utilize default object equality (A == {1} B == {1}, though A 15 | // and B contain the same thing, the container is not the same 16 | // referenced array instance... 17 | assertEquals(new int[]{1}.equals(new int[]{1}), __); 18 | } 19 | 20 | @Koan 21 | public void cloneEqualityIsNotRespected() { //! 22 | int[] original = new int[]{1}; 23 | assertEquals(original.equals(original.clone()), __); 24 | } 25 | 26 | @Koan 27 | public void anArraysHashCodeMethodDoesNotConsiderElements() { 28 | int[] array0 = new int[]{0}; 29 | int[] array1 = new int[]{0}; 30 | assertEquals(Integer.valueOf(array0.hashCode()).equals(array1.hashCode()), __); // not equal! 31 | // TODO: ponder the consequences when arrays are used in Hash Collection implementations. 32 | } 33 | 34 | @Koan 35 | public void arraysHelperClassEqualsMethodConsidersElementsWhenDeterminingEquality() { 36 | int[] array0 = new int[]{0}; 37 | int[] array1 = new int[]{0}; 38 | assertEquals(Arrays.equals(array0, array1), __); // whew - what most people assume 39 | // about equals in regard to arrays! (logical equality) 40 | } 41 | 42 | @Koan 43 | public void arraysHelperClassHashCodeMethodConsidersElementsWhenDeterminingHashCode() { 44 | int[] array0 = new int[]{0}; 45 | int[] array1 = new int[]{0}; 46 | // whew - what most people assume about hashCode in regard to arrays! 47 | assertEquals(Integer.valueOf(Arrays.hashCode(array0)).equals(Arrays.hashCode(array1)), __); 48 | } 49 | 50 | @Koan 51 | public void arraysAreMutable() { 52 | final boolean[] oneBoolean = new boolean[]{false}; 53 | oneBoolean[0] = true; 54 | assertEquals(oneBoolean[0], __); 55 | } 56 | 57 | @Koan 58 | public void arraysAreIndexedAtZero() { 59 | int[] integers = new int[]{1, 2}; 60 | assertEquals(integers[0], __); 61 | assertEquals(integers[1], __); 62 | } 63 | 64 | @Koan 65 | public void arrayIndexOutOfBounds() { 66 | int[] array = new int[]{1}; 67 | @SuppressWarnings("unused") 68 | int meh = array[1]; // remember 0 based indexes, 1 is the 2nd element (which doesn't exist) 69 | } 70 | 71 | @Koan 72 | public void arrayLengthCanBeChecked() { 73 | assertEquals(new int[1].length, __); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutAssertions.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.*; 7 | 8 | public class AboutAssertions { 9 | 10 | @Koan 11 | public void assertBooleanTrue() { 12 | // there are two possibilities, true or false, what would it be here? 13 | assertTrue(__); 14 | } 15 | 16 | @Koan 17 | public void assertBooleanFalse() { 18 | assertFalse(__); 19 | } 20 | 21 | @Koan 22 | public void assertNullObject() { 23 | // reference to the object can be null, a magic keyword, null, which means 24 | // that there is nothing there 25 | assertNull(__); 26 | } 27 | 28 | @Koan 29 | public void assertNullObjectReference() { 30 | Object someObject = __; 31 | assertNull(someObject); 32 | } 33 | 34 | @Koan 35 | public void assertNotNullObject() { 36 | // but what when there should not be a null value? 37 | assertNotNull(null); 38 | } 39 | 40 | @Koan 41 | public void assertEqualsUsingExpression() { 42 | assertTrue("Hello World!".equals(__)); 43 | } 44 | 45 | @Koan 46 | public void assertEqualsWithAFewExpressions() { 47 | assertEquals("Hello World!", __); 48 | assertEquals(1, __); 49 | assertEquals(2 + 2, __); 50 | assertEquals(2 * 3, __); 51 | assertEquals(3 - 8, __); 52 | assertEquals(10 / 2, __); 53 | } 54 | 55 | @Koan 56 | public void assertEqualsWithDescriptiveMessage() { 57 | // Generally, when using an assertXXX methods, expectation is on the 58 | // left and it is best practice to use a String for the first arg 59 | // indication what has failed 60 | assertEquals("The answer to 'life the universe and everything' should be 42", 42, __); 61 | } 62 | 63 | @Koan 64 | public void assertSameInstance() { 65 | Integer original = new Integer(1); 66 | Integer same = original; 67 | Integer different = new Integer(1); 68 | // These are both equal to the original... 69 | assertEquals(original, same); 70 | assertEquals(original, different); 71 | // ...but only one refers to the same instance as the original. 72 | assertSame(original, __); 73 | } 74 | 75 | @Koan 76 | public void assertNotSameInstance() { 77 | Integer original = new Integer(1); 78 | Integer same = original; 79 | Integer different = new Integer(1); 80 | // These are both equal to the original... 81 | assertEquals(original, same); 82 | assertEquals(original, different); 83 | // ...but only one of them refers to a different instance. 84 | assertNotSame(original, same); // We want equal, but _not_ the same. 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/behavior/Help.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline.behavior; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | 7 | import com.sandwich.koan.constant.ArgumentType; 8 | import com.sandwich.koan.constant.KoanConstants; 9 | import com.sandwich.koan.util.ApplicationUtils; 10 | 11 | 12 | public class Help extends AbstractArgumentBehavior { 13 | 14 | public void run(String...values) { 15 | StringBuilder sb = new StringBuilder( 16 | "Java Koans - HELP").append(KoanConstants.EOL); 17 | line(sb, "-----------------"); 18 | line(sb, "the following options are available:"); 19 | ArrayList argTypes = new ArrayList(Arrays.asList(getArgumentTypes())); 20 | Collections.reverse(argTypes); 21 | for(ArgumentType argType : argTypes){ 22 | StringBuilder stringArgs = new StringBuilder(); 23 | for(String arg : argType.args()){ 24 | stringArgs.append(arg); 25 | if(argType.args().indexOf(arg) < (argType.args().size() - 1)){ 26 | stringArgs.append(", "); 27 | } 28 | } 29 | String args = stringArgs.toString(); 30 | String fiveSpaces = spaces(5); 31 | sb.append(args).append(KoanConstants.EOL); 32 | sb.append(fiveSpaces).append(argType.name()).append(": ").append( 33 | makeCharWidth(70, getPositionOnCurrentLine(sb), argType.description())) 34 | .append(KoanConstants.EOL); 35 | } 36 | ApplicationUtils.getPresenter().displayMessage(sb.toString()); 37 | } 38 | 39 | private String makeCharWidth(int width, int positionOnCurrentLine, String text) { 40 | int upperIndexForLine = width - positionOnCurrentLine; 41 | if(upperIndexForLine > text.length()){ 42 | upperIndexForLine = text.length(); 43 | } 44 | StringBuilder sb = new StringBuilder(text.substring(0, upperIndexForLine)).append( 45 | KoanConstants.EOL); 46 | for(int i = upperIndexForLine; i < text.length(); i += width){ 47 | upperIndexForLine += width; 48 | if(upperIndexForLine > text.length()){ 49 | upperIndexForLine = text.length(); 50 | } 51 | sb.append(text.substring(i,upperIndexForLine)) 52 | .append(KoanConstants.EOL); 53 | } 54 | return sb.toString(); 55 | } 56 | 57 | private int getPositionOnCurrentLine(StringBuilder sb) { 58 | return getTextOnCurrentLine(sb).length(); 59 | } 60 | 61 | private String getTextOnCurrentLine(StringBuilder sb) { 62 | return sb.substring(sb.lastIndexOf(KoanConstants.EOL), sb.length()); 63 | } 64 | 65 | protected ArgumentType[] getArgumentTypes() { 66 | return ArgumentType.values(); 67 | } 68 | 69 | private String spaces(int i) { 70 | StringBuilder spaces = new StringBuilder(i); 71 | while(spaces.length() < i){ 72 | spaces.append(" "); 73 | } 74 | return spaces.toString(); 75 | } 76 | 77 | public void line(StringBuilder builder, String text){ 78 | builder.append(text).append(KoanConstants.EOL); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutAssertions.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.*; 7 | 8 | public class AboutAssertions { 9 | 10 | @Koan 11 | public void assertBooleanTrue() { 12 | // there are two possibilities, true or false, what would it be here? 13 | assertTrue(true); 14 | } 15 | 16 | @Koan 17 | public void assertBooleanFalse() { 18 | assertFalse(false); 19 | } 20 | 21 | @Koan 22 | public void assertNullObject() { 23 | // reference to the object can be null, a magic keyword, null, which means 24 | // that there is nothing there 25 | assertNull(null); 26 | } 27 | 28 | @Koan 29 | public void assertNullObjectReference() { 30 | Object someObject = null; 31 | assertNull(someObject); 32 | } 33 | 34 | @Koan 35 | public void assertNotNullObject() { 36 | // but what when there should not be a null value? 37 | assertNotNull("a"); 38 | } 39 | 40 | @Koan 41 | public void assertEqualsUsingExpression() { 42 | assertTrue("Hello World!".equals("Hello World!")); 43 | } 44 | 45 | @Koan 46 | public void assertEqualsWithAFewExpressions() { 47 | assertEquals("Hello World!", "Hello World!"); 48 | assertEquals(1, 1); 49 | assertEquals(2 + 2, 4); 50 | assertEquals(2 * 3, 6); 51 | assertEquals(3 - 8, -5); 52 | assertEquals(10 / 2, 5); 53 | } 54 | 55 | @Koan 56 | public void assertEqualsWithDescriptiveMessage() { 57 | // Generally, when using an assertXXX methods, expectation is on the 58 | // left and it is best practice to use a String for the first arg 59 | // indication what has failed 60 | assertEquals("The answer to 'life the universe and everything' should be 42", 42, 42); 61 | } 62 | 63 | @Koan 64 | public void assertSameInstance() { 65 | Integer original = new Integer(1); 66 | Integer same = original; 67 | Integer different = new Integer(1); 68 | // These are both equal to the original... 69 | assertEquals(original, same); 70 | assertEquals(original, different); 71 | // ...but only one refers to the same instance as the original. 72 | assertSame(original, original); 73 | } 74 | 75 | @Koan 76 | public void assertNotSameInstance() { 77 | Integer original = new Integer(1); 78 | Integer same = original; 79 | Integer different = new Integer(1); 80 | // These are both equal to the original... 81 | assertEquals(original, same); 82 | assertEquals(original, different); 83 | // ...but only one of them refers to a different instance. 84 | assertNotSame(original, different); // We want equal, but _not_ the same. 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/runner/AppLauncher.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.runner; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.Map; 6 | 7 | import com.sandwich.koan.ApplicationSettings; 8 | import com.sandwich.koan.cmdline.CommandLineArgument; 9 | import com.sandwich.koan.cmdline.CommandLineArgumentBuilder; 10 | import com.sandwich.koan.cmdline.CommandLineArgumentRunner; 11 | import com.sandwich.koan.constant.ArgumentType; 12 | import com.sandwich.koan.util.ApplicationUtils; 13 | import com.sandwich.util.io.FileMonitor; 14 | import com.sandwich.util.io.FileMonitorFactory; 15 | import com.sandwich.util.io.KoanFileCompileAndRunListener; 16 | import com.sandwich.util.io.directories.DirectoryManager; 17 | 18 | public class AppLauncher { 19 | 20 | public static void main(final String... args) throws Throwable { 21 | Map argsMap = new CommandLineArgumentBuilder( 22 | args); 23 | if (argsMap.containsKey(ArgumentType.RUN_KOANS)) { 24 | if (ApplicationSettings.isInteractive()) { 25 | new Thread(new Runnable() { 26 | public void run() { 27 | do { 28 | try { 29 | char c = (char) System.in.read(); 30 | char exitChar = ApplicationSettings.getExitChar(); 31 | if (Character.toUpperCase(c) == Character.toUpperCase(exitChar)) { 32 | exit(0); 33 | } 34 | } catch (IOException e) { 35 | throw new RuntimeException(e); 36 | } 37 | } while (true); 38 | } 39 | 40 | }).start(); 41 | FileMonitor monitor = FileMonitorFactory.getInstance(new File( 42 | DirectoryManager.getProdMainDir()), new File( 43 | DirectoryManager.getDataFile())); 44 | if (ApplicationUtils.isFirstTimeAppHasBeenRun()) { 45 | monitor.writeChanges(); 46 | } 47 | monitor.addFileSavedListener(new KoanFileCompileAndRunListener(argsMap)); 48 | } 49 | } 50 | new CommandLineArgumentRunner(argsMap).run(); 51 | if (ApplicationSettings.isDebug()) { 52 | StringBuilder argsBuilder = new StringBuilder(); 53 | int argNumber = 0; 54 | for (String arg : args) { 55 | argsBuilder.append("Argument number " 56 | + String.valueOf(++argNumber) + ": '" + arg + "'"); 57 | } 58 | ApplicationUtils.getPresenter().displayMessage( 59 | argsBuilder.toString()); 60 | } 61 | } 62 | 63 | static void exit(int status) { 64 | FileMonitorFactory.closeAll(); 65 | System.exit(status); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/filecompiler/CompilerConfig.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io.filecompiler; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.Collection; 7 | import java.util.Enumeration; 8 | import java.util.List; 9 | import java.util.ResourceBundle; 10 | 11 | public class CompilerConfig { 12 | 13 | private static final ResourceBundle commandBySuffixRB = ResourceBundle.getBundle("compilationcommands"); 14 | 15 | public static boolean isSourceFile(String fileName) { 16 | // TODO when supporting java > 5, change to return resourcebundle.containsKey(... 17 | Enumeration keys = commandBySuffixRB.getKeys(); 18 | String suffix = getSuffix(fileName).toLowerCase(); 19 | while(keys.hasMoreElements()){ 20 | String key = keys.nextElement(); 21 | if(key.equals(suffix)){ 22 | return true; 23 | } 24 | } 25 | return false; 26 | } 27 | 28 | public static String[] getCompilationCommand(File src, String destinationPath, String classPath) { 29 | String absolutePath = src.getAbsolutePath(); 30 | String command = commandBySuffixRB.getString(getSuffix(absolutePath)); 31 | if(command == null){ 32 | throw new RuntimeException("Do not know how to compile " + absolutePath); 33 | } 34 | List splitCommand = Arrays.asList(command.split(" ")); 35 | List commandSegments = new ArrayList(); 36 | for(String segment : splitCommand){ 37 | String lowerCaseSegment = segment.toLowerCase(); 38 | if("${bindir}".equals(lowerCaseSegment)){ 39 | commandSegments.add(destinationPath); 40 | }else if("${classpath}".equals(lowerCaseSegment)){ 41 | commandSegments.add(classPath); 42 | }else if("${filename}".equals(lowerCaseSegment)){ 43 | commandSegments.add(src.getAbsolutePath()); 44 | }else{ 45 | commandSegments.add(segment); 46 | } 47 | } 48 | return commandSegments.toArray(new String[commandSegments.size()]); 49 | } 50 | 51 | public static Collection getSupportedFileSuffixes() { 52 | Enumeration keysEnumeration = commandBySuffixRB.getKeys(); 53 | Collection keys = new ArrayList(); 54 | while(keysEnumeration.hasMoreElements()){ 55 | keys.add(keysEnumeration.nextElement()); 56 | } 57 | return keys; 58 | } 59 | 60 | public static String getSuffix(String fileName) { 61 | if(fileName != null){ 62 | int periodIndex = fileName.lastIndexOf('.'); 63 | if(periodIndex > -1){ 64 | return fileName.substring(periodIndex).toLowerCase(); 65 | } 66 | } 67 | return ""; 68 | } 69 | 70 | public static boolean isSuffixSupported(String suffix) { 71 | return getSupportedFileSuffixes().contains(suffix); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/Strings.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util; 2 | import static com.sandwich.koan.constant.KoanConstants.PERIOD; 3 | 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.util.Locale; 7 | import java.util.MissingResourceException; 8 | import java.util.PropertyResourceBundle; 9 | import java.util.ResourceBundle; 10 | import java.util.logging.Level; 11 | import java.util.logging.Logger; 12 | 13 | import com.sandwich.util.io.directories.DirectoryManager; 14 | 15 | public class Strings { 16 | 17 | private static final ResourceBundle MESSAGES_BUNDLE = getMessagesBundle(); 18 | private static final String UNFOUND_PROP_VALUE_STRING = "!"; 19 | 20 | private Strings() { 21 | } 22 | 23 | public static String getMessage(String key) { 24 | try { 25 | return MESSAGES_BUNDLE.getString(key); 26 | } catch (MissingResourceException e) { 27 | return new StringBuilder(UNFOUND_PROP_VALUE_STRING).append(key).append(UNFOUND_PROP_VALUE_STRING).toString(); 28 | } 29 | } 30 | 31 | public static String getMessage(Class clazz, String key){ 32 | return Strings.getMessage(new StringBuilder(clazz.getSimpleName()).append(PERIOD).append(key).toString()); 33 | } 34 | 35 | public static String[] getMessages(Class clazz, String key) { 36 | String[] tmp = getMessage(clazz, key).split(","); 37 | String[] trimmed = new String[tmp.length]; 38 | for(int i = 0; i < tmp.length; i++){ 39 | trimmed[i] = tmp[i].trim(); 40 | } 41 | return trimmed; 42 | } 43 | 44 | private static ResourceBundle getMessagesBundle(){ 45 | if(MESSAGES_BUNDLE == null){ 46 | return createResourceBundle(); 47 | } 48 | return MESSAGES_BUNDLE; 49 | } 50 | 51 | /** 52 | * conditionally create messages bundle for proper locale, not on classpath so need to handle manually 53 | * @return a resource bundle for default locale, or USA if default locale's language has no translated messages 54 | */ 55 | static ResourceBundle createResourceBundle() { 56 | ResourceBundle temp = null; 57 | try { 58 | temp = new PropertyResourceBundle(new FileInputStream( 59 | DirectoryManager.injectFileSystemSeparators( 60 | DirectoryManager.getProjectI18nDir(), 61 | new StringBuilder("messages_").append( 62 | Locale.getDefault().getLanguage()).append( 63 | ".properties").toString()))); 64 | } catch(FileNotFoundException x) { 65 | try { 66 | Logger.getLogger(Strings.class.getName()).log(Level.INFO, "Your default language is not supported yet. "+x.getLocalizedMessage()); 67 | temp = new PropertyResourceBundle(new FileInputStream( 68 | DirectoryManager.injectFileSystemSeparators( 69 | DirectoryManager.getProjectI18nDir(), "messages_en.properties"))); 70 | } catch (Exception e) { 71 | throw new RuntimeException(e); 72 | } 73 | } catch (Exception e) { 74 | throw new RuntimeException(e); 75 | } 76 | return temp; 77 | } 78 | 79 | public static boolean wasNotFound(String lesson) { 80 | return lesson.startsWith(UNFOUND_PROP_VALUE_STRING) && lesson.endsWith(UNFOUND_PROP_VALUE_STRING); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/FileMonitor.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Vector; 9 | 10 | import com.sandwich.koan.ApplicationSettings; 11 | 12 | public class FileMonitor { 13 | 14 | private final List listeners = new Vector(); 15 | private final DataFileHelper> fileHashesHelper; 16 | 17 | private final Map fileHashesByDirectory; 18 | 19 | final File fileSystemPath; 20 | 21 | public FileMonitor(File fileSystemPath, File dataFile) { 22 | this.fileSystemPath = fileSystemPath; 23 | if(!this.fileSystemPath.exists()){ 24 | throw new IllegalArgumentException(fileSystemPath+ " cannot be found."); 25 | } 26 | fileHashesHelper = new DataFileHelper>( 27 | dataFile, new HashMap()); 28 | fileHashesByDirectory = fileHashesHelper.read(); 29 | } 30 | 31 | public String getFilesystemPath() { 32 | return fileSystemPath.getAbsolutePath(); 33 | } 34 | 35 | public void close(){ 36 | listeners.clear(); 37 | } 38 | 39 | public void addFileSavedListener(FileListener listener){ 40 | listeners.add(listener); 41 | } 42 | 43 | public void removeFileSavedListener(FileListener listener){ 44 | listeners.remove(listener); 45 | } 46 | 47 | synchronized void notifyListeners(){ 48 | try { 49 | Map currentHashes = getFilesystemHashes(); 50 | for(String fileName : currentHashes.keySet()){ 51 | Long currentHash = currentHashes.get(fileName); 52 | Long previousHash = fileHashesByDirectory.get(fileName); 53 | 54 | fileHashesByDirectory.put(fileName, currentHash); 55 | File file = new File(fileName); 56 | 57 | for(FileListener listener : listeners){ 58 | if(previousHash == null && currentHash != null){ 59 | listener.newFile(file); 60 | }else if(currentHash == null && previousHash != null){ 61 | listener.fileDeleted(file); 62 | }else if(!currentHash.equals(previousHash)){ 63 | listener.fileSaved(file); 64 | } 65 | } 66 | } 67 | } catch (IOException e) { 68 | throw new RuntimeException(e); 69 | } 70 | } 71 | 72 | Map getFilesystemHashes() throws IOException { 73 | final HashMap fileHashes = new HashMap(); 74 | new ForEachFileAction(ApplicationSettings.getMonitorIgnorePattern()){ 75 | public void onFile(File src) throws IOException { 76 | fileHashes.put(src.getAbsolutePath(), src.lastModified()); 77 | } 78 | }.operate(fileSystemPath); 79 | return fileHashes; 80 | } 81 | 82 | public boolean isFileModifiedSinceLastPoll(String filePath, Long lastModified) { 83 | Long previousHash = fileHashesByDirectory.get(filePath); 84 | return !lastModified.equals(previousHash); 85 | } 86 | 87 | public void updateFileSaveTime(File file) { 88 | fileHashesByDirectory.put(file.getAbsolutePath(), file.lastModified()); 89 | } 90 | 91 | public void writeChanges() { 92 | try { 93 | fileHashesHelper.write(getFilesystemHashes()); 94 | } catch (IOException e) { 95 | e.printStackTrace(); 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /lib/src/test/java/com/sandwich/koan/runner/ui/AbstractSuitePresenterTest.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.runner.ui; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.fail; 5 | 6 | import java.util.Arrays; 7 | 8 | import org.junit.Test; 9 | 10 | import com.sandwich.koan.KoanMethod; 11 | import com.sandwich.koan.path.CommandLineTestCase; 12 | import com.sandwich.koan.result.KoanMethodResult; 13 | import com.sandwich.koan.result.KoanSuiteResult; 14 | import com.sandwich.koan.result.KoanSuiteResult.KoanResultBuilder; 15 | import com.sandwich.koan.suite.OneFailingKoan; 16 | import com.sandwich.koan.ui.AbstractSuitePresenter; 17 | 18 | public class AbstractSuitePresenterTest extends CommandLineTestCase { 19 | 20 | @Test 21 | public void testForwardingOneHundredPercentSuccess() throws Exception { 22 | final int state[] = new int[]{0}; 23 | AbstractSuitePresenter presenter = new AbstractSuitePresenter() { 24 | public void displayAllSuccess(KoanSuiteResult result) { 25 | assertEquals(0, state[0]); 26 | state[0] = 1; 27 | } 28 | public void displayChart(KoanSuiteResult result) { 29 | assertEquals(1, state[0]); 30 | state[0] = 2; 31 | } 32 | public void displayPassingFailing(KoanSuiteResult result) { 33 | assertEquals(2, state[0]); 34 | state[0] = 3; 35 | } 36 | public void displayHeader(KoanSuiteResult result) { 37 | assertEquals(3, state[0]); 38 | state[0] = 4; 39 | } 40 | public void displayOneOrMoreFailure(KoanSuiteResult result) { 41 | fail(); 42 | } 43 | public void displayError(String error) { 44 | fail(); 45 | } 46 | public void displayMessage(String error) { 47 | fail(); 48 | } 49 | public void clearMessages() { 50 | fail(); 51 | } 52 | }; 53 | 54 | KoanSuiteResult kr = new KoanResultBuilder().build(); 55 | presenter.displayResult(kr); 56 | assertEquals(4, state[0]); 57 | } 58 | 59 | @Test 60 | public void testForwardingOneOrMoreFails() throws Exception { 61 | final int state[] = new int[]{0}; 62 | AbstractSuitePresenter presenter = new AbstractSuitePresenter() { 63 | public void displayOneOrMoreFailure(KoanSuiteResult result) { 64 | assertEquals(0, state[0]); 65 | state[0] = 1; 66 | } 67 | public void displayChart(KoanSuiteResult result) { 68 | assertEquals(1, state[0]); 69 | state[0] = 2; 70 | } 71 | public void displayPassingFailing(KoanSuiteResult result) { 72 | assertEquals(2, state[0]); 73 | state[0] = 3; 74 | } 75 | public void displayHeader(KoanSuiteResult result) { 76 | assertEquals(3, state[0]); 77 | state[0] = 4; 78 | } 79 | public void displayAllSuccess(KoanSuiteResult result) { 80 | fail(); 81 | } 82 | public void displayError(String error) { 83 | fail(); 84 | } 85 | public void displayMessage(String error) { 86 | fail(); 87 | } 88 | public void clearMessages() { 89 | fail(); 90 | } 91 | }; 92 | 93 | KoanSuiteResult kr = new KoanResultBuilder().remainingCases(Arrays.asList(OneFailingKoan.class.getSimpleName()) 94 | ).methodResult(new KoanMethodResult(KoanMethod.getInstance("", OneFailingKoan.class.getDeclaredMethods()[0]), 95 | "", "")).build(); 96 | presenter.displayResult(kr); 97 | assertEquals(4, state[0]); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/util/io/DataFileHelper.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.util.io; 2 | 3 | import java.io.EOFException; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.ObjectInputStream; 9 | import java.io.ObjectOutputStream; 10 | import java.io.UTFDataFormatException; 11 | 12 | /** 13 | * Handles persistence to/from a filesystem, and makes assumption instances of T 14 | * are mutated after retrieval. Their mutations are saved to file on app 15 | * shutdown automatically. 16 | * 17 | * @param 18 | */ 19 | public class DataFileHelper { 20 | 21 | private File dataFile; 22 | private T lastRetrieval; 23 | private T defaultState; 24 | 25 | public DataFileHelper(File dataFile, T defaultState){ 26 | this.dataFile = dataFile; 27 | if(!dataFile.exists()){ 28 | dataFile.getParentFile().mkdirs(); 29 | write(defaultState); 30 | } 31 | this.defaultState = defaultState; 32 | Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 33 | public void run() { 34 | if(lastRetrieval != null){ 35 | write(lastRetrieval); 36 | } 37 | } 38 | })); 39 | } 40 | 41 | @SuppressWarnings("unchecked") 42 | public T read(){ 43 | ObjectInputStream objectInputStream = null; 44 | try { 45 | File dataFile = getDataFile(); 46 | if(!dataFile.exists()){ 47 | return null; 48 | } 49 | objectInputStream = new ObjectInputStream(new FileInputStream(dataFile)); 50 | if(dataFile.exists()){ 51 | try { 52 | return lastRetrieval = (T)objectInputStream.readObject(); 53 | }catch(UTFDataFormatException x){ 54 | createNewFile(dataFile); 55 | return defaultState; 56 | }catch(EOFException x){ 57 | createNewFile(dataFile); 58 | return defaultState; 59 | } 60 | }else{ 61 | return null; 62 | } 63 | } catch (Exception e) { 64 | try{ 65 | if(objectInputStream != null){ 66 | objectInputStream.close(); 67 | } 68 | }catch(Exception e2){ 69 | throw new RuntimeException(e2); 70 | } 71 | throw new RuntimeException(e); 72 | } finally { 73 | if(objectInputStream != null){ 74 | try { 75 | objectInputStream.close(); 76 | } catch (IOException e) { 77 | throw new RuntimeException(e); 78 | } 79 | } 80 | } 81 | } 82 | 83 | private void createNewFile(File dataFile) throws IOException { 84 | dataFile.delete(); 85 | dataFile.createNewFile(); 86 | write(defaultState); 87 | } 88 | 89 | public void write(T state){ 90 | ObjectOutputStream objectOutputStream = null; 91 | try { 92 | objectOutputStream = new ObjectOutputStream(new FileOutputStream(getDataFile())); 93 | objectOutputStream.writeObject(state); 94 | } catch (Exception e) { 95 | try{ 96 | if(objectOutputStream != null){ 97 | objectOutputStream.close(); 98 | } 99 | }catch(Exception e2){ 100 | throw new RuntimeException(e2); 101 | } 102 | throw new RuntimeException(e); 103 | } finally { 104 | if(objectOutputStream != null){ 105 | try { 106 | objectOutputStream.close(); 107 | } catch (IOException e) { 108 | throw new RuntimeException(e); 109 | } 110 | } 111 | } 112 | } 113 | 114 | private File getDataFile() { 115 | return dataFile; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/KoanMethod.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; 6 | import com.sandwich.koan.path.xmltransformation.RbVariableInjector; 7 | import com.sandwich.util.io.KoanSuiteCompilationListener; 8 | 9 | public class KoanMethod { 10 | 11 | private final transient Method method; 12 | private final String lesson; 13 | private final boolean displayIncompleteException; 14 | private static final KoanSuiteCompilationListener listener = new KoanSuiteCompilationListener(); 15 | 16 | private KoanMethod(KoanElementAttributes koanAttributes) throws SecurityException, NoSuchMethodException{ 17 | this(null, koanAttributes); 18 | } 19 | 20 | public static KoanMethod getInstance(KoanElementAttributes koanAttributes){ 21 | try { 22 | return new KoanMethod(koanAttributes); 23 | } catch (Exception e) { 24 | throw new RuntimeException(e); 25 | } 26 | } 27 | 28 | public static KoanMethod getInstance(Method method){ 29 | return new KoanMethod(null, method, true); 30 | } 31 | 32 | public static KoanMethod getInstance(String lesson, Method method){ 33 | return new KoanMethod(lesson, method, true); 34 | } 35 | 36 | private KoanMethod(String lesson, Method method, boolean displayIncompleteException){ 37 | if(method == null){ 38 | throw new IllegalArgumentException("method may not be null"); 39 | } 40 | this.method = method; 41 | this.lesson = new RbVariableInjector(lesson, method).injectLessonVariables(); 42 | this.displayIncompleteException = displayIncompleteException; 43 | } 44 | 45 | public KoanMethod(String lesson, KoanElementAttributes koanAttributes) throws SecurityException, NoSuchMethodException { 46 | this( lesson, 47 | KoanClassLoader.getInstance().loadClass(koanAttributes.className, listener) 48 | .getMethod(koanAttributes.name), 49 | !"false".equalsIgnoreCase(koanAttributes.displayIncompleteKoanException)); 50 | } 51 | 52 | public String getLesson() { 53 | return lesson; 54 | } 55 | 56 | public Method getMethod() { 57 | return method; 58 | } 59 | 60 | public boolean displayIncompleteException() { 61 | return displayIncompleteException; 62 | } 63 | 64 | public KoanMethod clone(Method method){ 65 | return new KoanMethod(lesson, method, displayIncompleteException); 66 | } 67 | 68 | @Override public String toString(){ 69 | return "{"+getMethod().getName() 70 | +" : "+ (lesson.length() > 20 ? lesson.substring(0, 20) + "..." : lesson)+"}"; 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | final int prime = 31; 76 | int result = 1; 77 | result = prime * result + (displayIncompleteException ? 1231 : 1237); 78 | result = prime * result + ((lesson == null) ? 0 : lesson.hashCode()); 79 | return result; 80 | } 81 | 82 | @Override 83 | public boolean equals(Object obj) { 84 | if (this == obj) 85 | return true; 86 | if (obj == null) 87 | return false; 88 | if (getClass() != obj.getClass()) 89 | return false; 90 | KoanMethod other = (KoanMethod) obj; 91 | if (displayIncompleteException != other.displayIncompleteException) 92 | return false; 93 | if (lesson == null) { 94 | if (other.lesson != null) 95 | return false; 96 | } else if (!lesson.equals(other.lesson)) 97 | return false; 98 | return true; 99 | } 100 | 101 | 102 | } 103 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgumentBuilder.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.cmdline; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedHashMap; 5 | import java.util.List; 6 | import java.util.Map.Entry; 7 | import java.util.logging.Logger; 8 | 9 | import com.sandwich.koan.ApplicationSettings; 10 | import com.sandwich.koan.constant.ArgumentType; 11 | import com.sandwich.koan.ui.SuitePresenter; 12 | import com.sandwich.koan.util.ApplicationUtils; 13 | 14 | public class CommandLineArgumentBuilder extends LinkedHashMap { 15 | 16 | private static final long serialVersionUID = 7635285665311420603L; 17 | 18 | public CommandLineArgumentBuilder(String...args){ 19 | args = filterOutNullElements(args); 20 | if(args.length == 0){ 21 | put(ArgumentType.RUN_KOANS, new CommandLineArgument(ArgumentType.RUN_KOANS, new String[0])); 22 | } else if (args.length == 1 && ArgumentType.findTypeByString(args[0]) == null){ 23 | put(ArgumentType.CLASS_ARG, new CommandLineArgument(ArgumentType.CLASS_ARG, args[0])); 24 | } else if (args.length == 2 && ArgumentType.findTypeByString(args[0]) == null && ArgumentType.findTypeByString(args[1]) == null){ 25 | put(ArgumentType.CLASS_ARG, new CommandLineArgument(ArgumentType.CLASS_ARG, args[0])); 26 | put(ArgumentType.METHOD_ARG, new CommandLineArgument(ArgumentType.METHOD_ARG, args[1])); 27 | } else { 28 | ArgumentType type = null; 29 | List params = null; 30 | for(int index = 0; index < args.length; index++){ 31 | ArgumentType tmpType = ArgumentType.findTypeByString(args[index]); 32 | if(tmpType == null){ 33 | if(type == null){ 34 | Logger.getAnonymousLogger().warning("The argument: " + args[index] + " is not recognized, it will be ignored"); 35 | }else{ 36 | params.add(args[index]); 37 | } 38 | }else{ 39 | if(type == null){ 40 | type = tmpType; 41 | params = new ArrayList(); 42 | }else{ 43 | put(type, new CommandLineArgument(type, params.toArray(new String[params.size()]))); 44 | type = null; 45 | params = new ArrayList(); 46 | } 47 | } 48 | } 49 | if(type != null && params != null){ 50 | put(type, new CommandLineArgument(type, params.toArray(new String[params.size()]))); 51 | } 52 | } 53 | applyAssumedStartupBehaviors(); 54 | } 55 | 56 | private String[] filterOutNullElements(String... args) { 57 | List tempArgs = new ArrayList(); 58 | for(String arg : args){ 59 | if(arg != null && arg.trim().length() > 0){ 60 | tempArgs.add(arg.trim()); 61 | } 62 | } 63 | return tempArgs.toArray(new String[tempArgs.size()]); 64 | } 65 | 66 | void applyAssumedStartupBehaviors() { 67 | if(ApplicationUtils.isFirstTimeAppHasBeenRun()){ 68 | ArgumentType.BACKUP.run(new String[0]); 69 | ApplicationUtils.getPresenter().clearMessages(); 70 | } 71 | if(isEmpty() || !containsKey(ArgumentType.RUN_KOANS) && ( 72 | containsKey(ArgumentType.CLASS_ARG) || 73 | containsKey(ArgumentType.DEBUG))){ 74 | if(ApplicationSettings.isDebug()){ 75 | SuitePresenter presenter = ApplicationUtils.getPresenter(); 76 | presenter.displayMessage("Planting default run target."); 77 | for(Entry argEntry : entrySet()){ 78 | presenter.displayMessage("Key: '"+argEntry.getKey()+"'"); 79 | presenter.displayMessage("Value: '"+argEntry.getValue()+"'"); 80 | } 81 | } 82 | put(ArgumentType.RUN_KOANS, new CommandLineArgument(ArgumentType.RUN_KOANS, true, new String[0])); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /koans/src/beginner/AboutInheritance.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutInheritance { 9 | 10 | abstract class Animal { 11 | abstract public String makeSomeNoise(); 12 | } 13 | 14 | class Cow extends Animal { 15 | @Override 16 | public String makeSomeNoise() { 17 | return "Moo!"; 18 | } 19 | } 20 | 21 | class Dog extends Animal { 22 | @Override 23 | public String makeSomeNoise() { 24 | return "Woof!"; 25 | } 26 | 27 | public boolean canFetch() { 28 | return true; 29 | } 30 | } 31 | 32 | class Puppy extends Dog { 33 | @Override 34 | public String makeSomeNoise() { 35 | return "Squeak!"; 36 | } 37 | public boolean canFetch() { 38 | return false; 39 | } 40 | } 41 | 42 | @Koan 43 | public void methodOverloading() { 44 | Cow bob = new Cow(); 45 | Dog max = new Dog(); 46 | Puppy barney = new Puppy(); 47 | assertEquals(bob.makeSomeNoise(), __); 48 | assertEquals(max.makeSomeNoise(), __); 49 | assertEquals(barney.makeSomeNoise(), __); 50 | 51 | assertEquals(max.canFetch(), __); 52 | assertEquals(barney.canFetch(), __); 53 | // but can Bob the Cow fetch? 54 | } 55 | 56 | @Koan 57 | public void methodOverloadingUsingPolymorphism() { 58 | Animal bob = new Cow(); 59 | Animal max = new Dog(); 60 | Animal barney = new Puppy(); 61 | assertEquals(bob.makeSomeNoise(), __); 62 | assertEquals(max.makeSomeNoise(), __); 63 | assertEquals(barney.makeSomeNoise(), __); 64 | // but can max or barney (here as an Animal) fetch? 65 | // try to write it down here 66 | } 67 | 68 | @Koan 69 | public void inheritanceHierarchy() { 70 | Animal someAnimal = new Cow(); 71 | Animal bob = new Cow(); 72 | assertEquals(someAnimal.makeSomeNoise().equals(bob.makeSomeNoise()), __); 73 | // cow is a Cow, but it can also be an animal 74 | assertEquals(bob instanceof Animal, __); 75 | assertEquals(bob instanceof Cow, __); 76 | // but is it a Puppy? 77 | assertEquals(bob instanceof Puppy, __); 78 | } 79 | 80 | @Koan 81 | public void deeperInheritanceHierarchy() { 82 | Dog max = new Dog(); 83 | Puppy barney = new Puppy(); 84 | assertEquals(max instanceof Puppy, __); 85 | assertEquals(max instanceof Dog, __); 86 | assertEquals(barney instanceof Puppy, __); 87 | assertEquals(barney instanceof Dog, __); 88 | } 89 | 90 | // TODO overriding 91 | // 92 | // abstract class ParentTwo { 93 | // abstract public Collection doStuff(); 94 | // } 95 | // 96 | // class ChildTwo extends ParentTwo { 97 | // public Collection doStuff() { 98 | // return Collections.emptyList(); 99 | // } 100 | // 101 | // ; 102 | // } 103 | // 104 | // @Koan 105 | // public void overriddenMethodsMayReturnSubtype() { 106 | // // What do you need to change in order to get rid of the type cast? 107 | // // Why does this work? 108 | // List list = (List) new ChildTwo().doStuff(); 109 | // assertEquals(list instanceof List, __); 110 | // } 111 | } 112 | -------------------------------------------------------------------------------- /koans/app/data/src/beginner/AboutInheritance.java: -------------------------------------------------------------------------------- 1 | package beginner; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import static com.sandwich.koan.constant.KoanConstants.__; 6 | import static com.sandwich.util.Assert.assertEquals; 7 | 8 | public class AboutInheritance { 9 | 10 | abstract class Animal { 11 | abstract public String makeSomeNoise(); 12 | } 13 | 14 | class Cow extends Animal { 15 | @Override 16 | public String makeSomeNoise() { 17 | return "Moo!"; 18 | } 19 | } 20 | 21 | class Dog extends Animal { 22 | @Override 23 | public String makeSomeNoise() { 24 | return "Woof!"; 25 | } 26 | 27 | public boolean canFetch() { 28 | return true; 29 | } 30 | } 31 | 32 | class Puppy extends Dog { 33 | @Override 34 | public String makeSomeNoise() { 35 | return "Squeak!"; 36 | } 37 | public boolean canFetch() { 38 | return false; 39 | } 40 | } 41 | 42 | @Koan 43 | public void methodOverloading() { 44 | Cow bob = new Cow(); 45 | Dog max = new Dog(); 46 | Puppy barney = new Puppy(); 47 | assertEquals(bob.makeSomeNoise(), __); 48 | assertEquals(max.makeSomeNoise(), __); 49 | assertEquals(barney.makeSomeNoise(), __); 50 | 51 | assertEquals(max.canFetch(), __); 52 | assertEquals(barney.canFetch(), __); 53 | // but can Bob the Cow fetch? 54 | } 55 | 56 | @Koan 57 | public void methodOverloadingUsingPolymorphism() { 58 | Animal bob = new Cow(); 59 | Animal max = new Dog(); 60 | Animal barney = new Puppy(); 61 | assertEquals(bob.makeSomeNoise(), __); 62 | assertEquals(max.makeSomeNoise(), __); 63 | assertEquals(barney.makeSomeNoise(), __); 64 | // but can max or barney (here as an Animal) fetch? 65 | // try to write it down here 66 | } 67 | 68 | @Koan 69 | public void inheritanceHierarchy() { 70 | Animal someAnimal = new Cow(); 71 | Animal bob = new Cow(); 72 | assertEquals(someAnimal.makeSomeNoise().equals(bob.makeSomeNoise()), __); 73 | // cow is a Cow, but it can also be an animal 74 | assertEquals(bob instanceof Animal, __); 75 | assertEquals(bob instanceof Cow, __); 76 | // but is it a Puppy? 77 | assertEquals(bob instanceof Puppy, __); 78 | } 79 | 80 | @Koan 81 | public void deeperInheritanceHierarchy() { 82 | Dog max = new Dog(); 83 | Puppy barney = new Puppy(); 84 | assertEquals(max instanceof Puppy, __); 85 | assertEquals(max instanceof Dog, __); 86 | assertEquals(barney instanceof Puppy, __); 87 | assertEquals(barney instanceof Dog, __); 88 | } 89 | 90 | // TODO overriding 91 | // 92 | // abstract class ParentTwo { 93 | // abstract public Collection doStuff(); 94 | // } 95 | // 96 | // class ChildTwo extends ParentTwo { 97 | // public Collection doStuff() { 98 | // return Collections.emptyList(); 99 | // } 100 | // 101 | // ; 102 | // } 103 | // 104 | // @Koan 105 | // public void overriddenMethodsMayReturnSubtype() { 106 | // // What do you need to change in order to get rid of the type cast? 107 | // // Why does this work? 108 | // List list = (List) new ChildTwo().doStuff(); 109 | // assertEquals(list instanceof List, __); 110 | // } 111 | } 112 | -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/runner/KoanMethodRunner.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.runner; 2 | 3 | import static com.sandwich.koan.constant.KoanConstants.EOLS; 4 | import static com.sandwich.koan.constant.KoanConstants.EXPECTATION_LEFT_ARG; 5 | import static com.sandwich.koan.constant.KoanConstants.EXPECTED_LEFT; 6 | import static com.sandwich.koan.constant.KoanConstants.EXPECTED_RIGHT; 7 | import static com.sandwich.koan.constant.KoanConstants.__; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.logging.Logger; 11 | 12 | import com.sandwich.koan.KoanIncompleteException; 13 | import com.sandwich.koan.KoanMethod; 14 | import com.sandwich.koan.cmdline.CommandLineArgumentRunner; 15 | import com.sandwich.koan.constant.KoanConstants; 16 | import com.sandwich.koan.result.KoanMethodResult; 17 | import com.sandwich.util.ExceptionUtils; 18 | import com.sandwich.util.Strings; 19 | import com.sandwich.util.io.directories.DirectoryManager; 20 | import com.sandwich.util.io.filecompiler.CompilerConfig; 21 | import com.sandwich.util.io.filecompiler.FileCompiler; 22 | 23 | public class KoanMethodRunner { 24 | 25 | private static final String EXPECTED_PROPERTY_KEY = "expected"; 26 | 27 | public static KoanMethodResult run(Object suite, KoanMethod koan){ 28 | try { 29 | Method method = koan.getMethod(); 30 | method.setAccessible(true); 31 | method.invoke(suite); 32 | } catch (Throwable t) { 33 | Throwable tempException = t; 34 | String message = ExceptionUtils.convertToPopulatedStackTraceString(t); 35 | while(tempException != null){ 36 | if(tempException instanceof KoanIncompleteException){ 37 | t = (KoanIncompleteException)tempException; 38 | message = t.getMessage(); 39 | if(message.contains(Strings.getMessage(EXPECTED_PROPERTY_KEY) + EXPECTED_LEFT + __ + EXPECTED_RIGHT)) { 40 | logExpectationOnWrongSideWarning(suite.getClass(), koan.getMethod()); 41 | } 42 | break; 43 | } 44 | tempException = tempException.getCause(); 45 | } 46 | return new KoanMethodResult(koan, message, getOriginalLineNumber(t, suite.getClass())); 47 | } 48 | return KoanMethodResult.PASSED; 49 | } 50 | 51 | private static void logExpectationOnWrongSideWarning(Class firstFailingSuite, Method firstFailingMethod) { 52 | Logger.getLogger(CommandLineArgumentRunner.class.getSimpleName()).severe( 53 | new StringBuilder( 54 | firstFailingSuite.getSimpleName()).append( 55 | ".").append( 56 | firstFailingMethod.getName()).append( 57 | " ").append( 58 | EXPECTATION_LEFT_ARG).toString()); 59 | } 60 | 61 | /** 62 | * Return the line number found closest to the point of failure, with a reference to the 63 | * failing suite's classname. 64 | * 65 | * @param t 66 | * @param failingSuite 67 | * @return 68 | */ 69 | static String getOriginalLineNumber(Throwable t, Class failingSuite){ 70 | if(failingSuite != null && 71 | CompilerConfig.getSuffix(FileCompiler.getSourceFileFromClass(DirectoryManager.getSourceDir(), failingSuite.getName()).getAbsolutePath()) 72 | .equals(".java")){ 73 | String[] lines = ExceptionUtils.convertToPopulatedStackTraceString(t).split(EOLS); 74 | for(int i = lines.length - 1; i >= 0; --i){ 75 | String line = lines[i]; 76 | if(line.contains(failingSuite.getName())){ 77 | int start = line.indexOf(KoanConstants.LINE_NO_START)+KoanConstants.LINE_NO_START.length(); 78 | int end = line.lastIndexOf(KoanConstants.LINE_NO_END); 79 | end = end > line.length() ? line.length() : end; 80 | return line.substring(start, end); 81 | } 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /lib/src/main/java/com/sandwich/koan/constant/ArgumentType.java: -------------------------------------------------------------------------------- 1 | package com.sandwich.koan.constant; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.logging.Logger; 9 | 10 | import com.sandwich.koan.cmdline.behavior.ArgumentBehavior; 11 | import com.sandwich.koan.cmdline.behavior.Backup; 12 | import com.sandwich.koan.cmdline.behavior.ClassArg; 13 | import com.sandwich.koan.cmdline.behavior.Clear; 14 | import com.sandwich.koan.cmdline.behavior.Debug; 15 | import com.sandwich.koan.cmdline.behavior.Help; 16 | import com.sandwich.koan.cmdline.behavior.MethodArg; 17 | import com.sandwich.koan.cmdline.behavior.Reset; 18 | import com.sandwich.koan.runner.RunKoans; 19 | import com.sandwich.koan.util.ApplicationUtils; 20 | import com.sandwich.util.Strings; 21 | 22 | 23 | public enum ArgumentType implements ArgumentBehavior { 24 | 25 | HELP( Help.class), 26 | RESET( Reset.class), 27 | BACKUP( Backup.class), 28 | DEBUG( Debug.class), 29 | CLEAR( Clear.class), 30 | //TEST( Test.class), 31 | // important class MUST come before method - due to how Enum implements comparable and order 32 | // dependent logic later @ see ArgumentTypeTest.testClassPrecedesMethod 33 | CLASS_ARG( ClassArg.class), 34 | METHOD_ARG( MethodArg.class), 35 | RUN_KOANS( RunKoans.class); 36 | 37 | private static final String DESCRIPTION = "description"; 38 | private static final String ARGUMENTS = "args"; 39 | 40 | private final List args; 41 | private final ArgumentBehavior behavior; 42 | private final String description; 43 | 44 | ArgumentType(Class c){ 45 | try{ 46 | this.behavior = c.newInstance(); 47 | }catch(Throwable t){ 48 | throw new RuntimeException(t); 49 | } 50 | this.args = Arrays.asList(Strings.getMessages(c, ARGUMENTS)); 51 | this.description = Strings.getMessage(c, DESCRIPTION); 52 | } 53 | public List args(){ 54 | return args; 55 | } 56 | public String description(){ 57 | return description; 58 | } 59 | private static final Map TYPES_BY_STRING; 60 | static{ 61 | Map types = new HashMap(); 62 | for(ArgumentType type : ArgumentType.values()){ 63 | for(String arg : type.args()){ 64 | if(types.containsKey(arg)){ 65 | throw new IllegalArgumentException( 66 | Strings.getMessages(ArgumentType.class,"duplicated_arg_error_part1")+arg+ 67 | Strings.getMessages(ArgumentType.class,"duplicated_arg_error_part2")); //$NON-NLS-1$ //$NON-NLS-2$ 68 | } 69 | types.put(arg, type); 70 | } 71 | } 72 | TYPES_BY_STRING = Collections.unmodifiableMap(types); 73 | } 74 | public void run(String... values) { 75 | try{ 76 | behavior.run(values); 77 | ApplicationUtils.getPresenter().displayMessage(behavior.getSuccessMessage()); 78 | }catch(Throwable t){ 79 | if(behavior instanceof RunKoans){ 80 | if(t instanceof RuntimeException){ 81 | throw (RuntimeException)t; 82 | }else{ 83 | throw new RuntimeException(t); 84 | } 85 | }else{ 86 | Logger.getAnonymousLogger().severe(t.getLocalizedMessage()); 87 | ApplicationUtils.getPresenter().displayError(behavior.getErrorMessage()); 88 | } 89 | } 90 | } 91 | public String getErrorMessage() { 92 | return behavior.getErrorMessage(); 93 | } 94 | public String getSuccessMessage() { 95 | return behavior.getSuccessMessage(); 96 | } 97 | public static ArgumentType findTypeByString(String stringArg) { 98 | return TYPES_BY_STRING.get(stringArg); 99 | } 100 | } 101 | 102 | 103 | -------------------------------------------------------------------------------- /koans/src/java7/AboutTryWithResources.java: -------------------------------------------------------------------------------- 1 | package java7; 2 | 3 | import com.sandwich.koan.Koan; 4 | 5 | import java.io.*; 6 | 7 | import static com.sandwich.koan.constant.KoanConstants.__; 8 | import static com.sandwich.util.Assert.assertEquals; 9 | 10 | public class AboutTryWithResources { 11 | 12 | class AutoClosableResource implements AutoCloseable { 13 | public void foo() throws WorkException { 14 | throw new WorkException("Exception thrown while working"); 15 | } 16 | 17 | public void close() throws CloseException { 18 | throw new CloseException("Exception thrown while closing"); 19 | } 20 | } 21 | 22 | class WorkException extends Exception { 23 | public WorkException(String message) { 24 | super(message); 25 | } 26 | } 27 | 28 | class CloseException extends Exception { 29 | public CloseException(String message) { 30 | super(message); 31 | } 32 | } 33 | 34 | @Koan 35 | public void lookMaNoClose() { 36 | String str = "first line" 37 | + System.lineSeparator() 38 | + "second line"; 39 | InputStream is = new ByteArrayInputStream(str.getBytes()); 40 | String line; 41 | /* BufferedReader implementing @see java.lang.AutoCloseable interface */ 42 | try (BufferedReader br = 43 | new BufferedReader( 44 | new InputStreamReader(is))) { 45 | line = br.readLine(); 46 | //br guaranteed to be closed 47 | } catch (IOException e) { 48 | line = "error"; 49 | } 50 | assertEquals(line, __); 51 | } 52 | 53 | @Koan 54 | public void lookMaNoCloseWithException() throws IOException { 55 | String line = "no need to close readers"; 56 | try (BufferedReader br = 57 | new BufferedReader( 58 | new FileReader("I do not exist!"))) { 59 | line = br.readLine(); 60 | } catch (FileNotFoundException e) { 61 | line = "no more leaking!"; 62 | } 63 | assertEquals(line, __); 64 | } 65 | 66 | @Koan 67 | public void lookMaNoCloseWithMultipleResources() throws IOException { 68 | String str = "first line" 69 | + System.lineSeparator() 70 | + "second line"; 71 | InputStream is = new ByteArrayInputStream(str.getBytes()); 72 | String line; 73 | //multiple resources in the same try declaration 74 | try (BufferedReader br = 75 | new BufferedReader( 76 | new FileReader("I do not exist!")); 77 | BufferedReader brFromString = 78 | new BufferedReader( 79 | new InputStreamReader(is)) 80 | ) { 81 | line = br.readLine(); 82 | line += brFromString.readLine(); 83 | } catch (IOException e) { 84 | line = "error"; 85 | } 86 | assertEquals(line, __); 87 | } 88 | 89 | @Koan 90 | public void supressException() { 91 | String message = ""; 92 | try { 93 | bar(); 94 | } catch (WorkException e) { 95 | message += e.getMessage() + " " + e.getSuppressed()[0].getMessage(); 96 | } catch (CloseException e) { 97 | message += e.getMessage(); 98 | } 99 | assertEquals(message, __); 100 | } 101 | 102 | 103 | public void bar() throws CloseException, WorkException { 104 | try (AutoClosableResource autoClosableResource = 105 | new AutoClosableResource()) { 106 | autoClosableResource.foo(); 107 | } 108 | } 109 | } --------------------------------------------------------------------------------