├── CustomTest.java ├── CustomTester.java ├── Implementor ├── OldAdvancedImplementorTest.java ├── OldClassImplementorTest.java ├── OldInterfaceImplementorTest.java ├── basic │ ├── classes │ │ ├── AbstractClassWithInterface.java │ │ ├── package-info.java │ │ └── standard │ │ │ ├── ClassLogger.java │ │ │ ├── IIOException.java │ │ │ ├── IIOImage.java │ │ │ ├── Logger.java │ │ │ ├── RMIServerImpl.java │ │ │ ├── RelationNotFoundException.java │ │ │ └── package-info.java │ └── interfaces │ │ ├── InterfaceWithDefaultMethod.java │ │ ├── InterfaceWithStaticMethod.java │ │ ├── package-info.java │ │ └── standard │ │ ├── Accessible.java │ │ ├── Descriptor.java │ │ ├── RandomAccess.java │ │ └── package-info.java └── full │ ├── classes │ ├── ClassWithPackagePrivateConstructor.java │ ├── Overridden.java │ ├── package-info.java │ └── standard │ │ ├── BMPImageWriteParam.java │ │ ├── FileCacheImageInputStream.java │ │ ├── ImmutableDescriptor.java │ │ ├── LdapReferralException.java │ │ ├── RMIIIOPServerImpl.java │ │ └── package-info.java │ ├── interfaces │ ├── InterfaceWithoutMethods.java │ ├── Interfaces.java │ ├── Proxies.java │ ├── package-info.java │ └── standard │ │ ├── AccessibleAction.java │ │ ├── CachedRowSet.java │ │ ├── DataInput.java │ │ ├── DataOutput.java │ │ ├── SDeprecated.java │ │ └── package-info.java │ └── lang │ ├── Arabic.java │ ├── Greek.java │ ├── Hebrew.java │ ├── Russian.java │ ├── package-info.java │ └── ПриветInterface.java ├── NavigableSetTest.java ├── OldGroupQueryTest.java ├── OldRecursiveWalkTest.java ├── OldStudentQueryTest.java ├── OldWalkTest.java ├── README.md ├── SortedSetTest.java ├── img ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png └── 8.png ├── module-info.java ├── refre.sh └── submit.sh /CustomTest.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.ja.фамилия.walk; 2 | 3 | import info.kgeorgiy.java.advanced.walk.RecursiveWalkTest; 4 | import org.junit.Test; 5 | 6 | public class CustomTest extends RecursiveWalkTest { 7 | @Test 8 | public void test00_nothing() { 9 | // do nothing 10 | } 11 | } -------------------------------------------------------------------------------- /CustomTester.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.ja.фамилия.walk; 2 | 3 | import info.kgeorgiy.java.advanced.walk.RecursiveWalkTest; 4 | import info.kgeorgiy.java.advanced.walk.Tester; 5 | import info.kgeorgiy.java.advanced.walk.WalkTest; 6 | 7 | public class CustomTester extends Tester { 8 | 9 | public static void main(String[] args) { 10 | new CustomTester() 11 | .add("Custom", CustomTest.class) 12 | .add("Walk", OldWalkTest.class) 13 | .add("Recursive", OldRecursiveWalkTest.class) 14 | .run(args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Implementor/OldAdvancedImplementorTest.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor; 2 | 3 | import info.kgeorgiy.java.advanced.implementor.full.classes.Overridden; 4 | import info.kgeorgiy.java.advanced.implementor.full.interfaces.Interfaces; 5 | import info.kgeorgiy.java.advanced.implementor.full.interfaces.Proxies; 6 | import org.junit.Test; 7 | 8 | /** 9 | * Full tests for advanced version 10 | * of Implementor homework 11 | * for Java Advanced course. 12 | * 13 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 14 | */ 15 | public class OldAdvancedImplementorTest extends ClassImplementorTest { 16 | @Test 17 | public void test41_duplicateClasses() { 18 | test(false, Proxies.class); 19 | } 20 | 21 | @Test 22 | public void test42_nestedInterfaces() { 23 | test(false, Interfaces.OK); 24 | test(true, Interfaces.FAILED); 25 | } 26 | 27 | @Test 28 | public void test43_overridden() { 29 | test(false, Overridden.OK); 30 | test(true, Overridden.FAILED); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Implementor/OldClassImplementorTest.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor; 2 | 3 | import info.kgeorgiy.java.advanced.implementor.basic.classes.AbstractClassWithInterface; 4 | import info.kgeorgiy.java.advanced.implementor.basic.classes.standard.*; 5 | import info.kgeorgiy.java.advanced.implementor.full.classes.ClassWithPackagePrivateConstructor; 6 | import info.kgeorgiy.java.advanced.implementor.full.classes.standard.*; 7 | import org.junit.Test; 8 | 9 | import javax.annotation.processing.Completions; 10 | import java.util.Formatter; 11 | 12 | /** 13 | * Full tests for hard version 14 | * of Implementor homework 15 | * for Java Advanced course. 16 | * 17 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 18 | */ 19 | public class OldClassImplementorTest extends InterfaceImplementorTest { 20 | @Test 21 | public void test11_defaultConstructorClasses() { 22 | test(false, AbstractClassWithInterface.class, BMPImageWriteParam.class, RelationNotFoundException.class); 23 | } 24 | 25 | @Test 26 | public void test12_noDefaultConstructorClasses() { 27 | test(false, IIOException.class, ImmutableDescriptor.class, LdapReferralException.class, ClassLogger.class); 28 | } 29 | 30 | @Test 31 | public void test13_ambiguousConstructorClasses() { 32 | test(false, IIOImage.class); 33 | } 34 | 35 | @Test 36 | public void test14_utilityClasses() { 37 | test(true, Completions.class); 38 | } 39 | 40 | @Test 41 | public void test15_finalClasses() { 42 | test(true, Integer.class, String.class); 43 | } 44 | 45 | @Test 46 | public void test16_standardNonClasses() { 47 | test(true, void.class, String[].class, int[].class, String.class, boolean.class); 48 | } 49 | 50 | @Test 51 | public void test17_constructorThrows() { 52 | test(false, FileCacheImageInputStream.class); 53 | } 54 | 55 | @Test 56 | public void test18_nonPublicAbstractMethod() { 57 | test(false, RMIServerImpl.class, RMIIIOPServerImpl.class); 58 | } 59 | 60 | @Test 61 | public void test19_enum() { 62 | test(true, Enum.class, Formatter.BigDecimalLayoutForm.class); 63 | } 64 | 65 | @Test 66 | public void test20_packagePrivateConstructor() { 67 | test(false, ClassWithPackagePrivateConstructor.class); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Implementor/OldInterfaceImplementorTest.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor; 2 | 3 | import info.kgeorgiy.java.advanced.implementor.basic.classes.standard.Logger; 4 | import info.kgeorgiy.java.advanced.implementor.basic.interfaces.InterfaceWithDefaultMethod; 5 | import info.kgeorgiy.java.advanced.implementor.basic.interfaces.InterfaceWithStaticMethod; 6 | import info.kgeorgiy.java.advanced.implementor.basic.interfaces.standard.Accessible; 7 | import info.kgeorgiy.java.advanced.implementor.basic.interfaces.standard.Descriptor; 8 | import info.kgeorgiy.java.advanced.implementor.basic.interfaces.standard.RandomAccess; 9 | import info.kgeorgiy.java.advanced.implementor.full.interfaces.InterfaceWithoutMethods; 10 | import info.kgeorgiy.java.advanced.implementor.full.interfaces.Interfaces; 11 | import info.kgeorgiy.java.advanced.implementor.full.interfaces.Proxies; 12 | import info.kgeorgiy.java.advanced.implementor.full.interfaces.standard.*; 13 | import org.junit.Rule; 14 | import org.junit.Test; 15 | import org.junit.rules.TestWatcher; 16 | 17 | import java.nio.file.Paths; 18 | 19 | /** 20 | * Tests for easy version 21 | * of Implementor homework 22 | * for Java Advanced course. 23 | * 24 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 25 | */ 26 | public class OldInterfaceImplementorTest extends BaseImplementorTest { 27 | private String methodName; 28 | @Rule 29 | public TestWatcher watcher = watcher(description -> methodName = description.getMethodName()); 30 | 31 | @Test 32 | public void test01_constructor() { 33 | assertConstructor(Impler.class); 34 | } 35 | 36 | @Test 37 | public void test02_methodlessInterfaces() { 38 | test(false, RandomAccess.class, InterfaceWithoutMethods.class); 39 | } 40 | 41 | @Test 42 | public void test03_standardInterfaces() { 43 | test(false, Accessible.class, AccessibleAction.class, SDeprecated.class); 44 | } 45 | 46 | @Test 47 | public void test04_extendedInterfaces() { 48 | test(false, Descriptor.class, CachedRowSet.class, DataInput.class, DataOutput.class, Logger.class); 49 | } 50 | 51 | @Test 52 | public void test05_standardNonInterfaces() { 53 | test(true, void.class, String[].class, int[].class, String.class, boolean.class); 54 | } 55 | 56 | @Test 57 | public void test06_java8Interfaces() { 58 | test(false, InterfaceWithStaticMethod.class, InterfaceWithDefaultMethod.class); 59 | } 60 | 61 | @Test 62 | public void test07_duplicateClasses() { 63 | test(false, Proxies.class); 64 | } 65 | 66 | @Test 67 | public void test08_nestedInterfaces() { 68 | test(false, Interfaces.OK); 69 | test(true, Interfaces.FAILED); 70 | } 71 | 72 | protected void test(final boolean shouldFail, final Class... classes) { 73 | test(Paths.get(methodName), shouldFail, classes); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Implementor/basic/classes/AbstractClassWithInterface.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.classes; 2 | 3 | import java.io.DataInput; 4 | 5 | /** 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | public abstract class AbstractClassWithInterface implements DataInput { 9 | } 10 | -------------------------------------------------------------------------------- /Implementor/basic/classes/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Hand-made classes for basic tests 3 | * of Implementor homework 4 | * for Java Advanced course. 5 | * 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | package info.kgeorgiy.java.advanced.implementor.basic.classes; -------------------------------------------------------------------------------- /Implementor/basic/classes/standard/ClassLogger.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.classes.standard; 2 | 3 | import java.util.ResourceBundle; 4 | 5 | /** 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | public class ClassLogger implements Logger { 9 | 10 | private final String className; 11 | private final Logger logger; 12 | 13 | public ClassLogger(String subsystem, String className) { 14 | logger = null; 15 | this.className = className; 16 | } 17 | 18 | public final boolean traceOn() { 19 | return logger.isLoggable(Level.TRACE); 20 | } 21 | 22 | public final boolean debugOn() { 23 | return logger.isLoggable(Level.DEBUG); 24 | } 25 | 26 | public final boolean warningOn() { 27 | return logger.isLoggable(Level.WARNING); 28 | } 29 | 30 | public final boolean infoOn() { 31 | return logger.isLoggable(Level.INFO); 32 | } 33 | 34 | public final boolean configOn() { 35 | return logger.isLoggable(Level.DEBUG); 36 | } 37 | 38 | public final boolean fineOn() { 39 | return logger.isLoggable(Level.DEBUG); 40 | } 41 | 42 | public final boolean finerOn() { 43 | return logger.isLoggable(Level.TRACE); 44 | } 45 | 46 | public final boolean finestOn() { 47 | return logger.isLoggable(Level.TRACE); 48 | } 49 | 50 | public final void debug(String func, String msg) { 51 | logger.log(Level.DEBUG, msg); 52 | } 53 | 54 | public final void debug(String func, Throwable t) { 55 | logger.log(Level.DEBUG, className + "::" + func, t); 56 | } 57 | 58 | public final void debug(String func, String msg, Throwable t) { 59 | logger.log(Level.DEBUG, msg, t); 60 | } 61 | 62 | public final void trace(String func, String msg) { 63 | logger.log(Level.TRACE, msg); 64 | } 65 | 66 | public final void trace(String func, Throwable t) { 67 | logger.log(Level.TRACE, className + "::" + func, t); 68 | } 69 | 70 | public final void trace(String func, String msg, Throwable t) { 71 | logger.log(Level.TRACE, msg, t); 72 | } 73 | 74 | public final void error(String func, String msg) { 75 | logger.log(Level.ERROR, msg); 76 | } 77 | 78 | public final void error(String func, Throwable t) { 79 | logger.log(Level.ERROR, className + "::" + func, t); 80 | } 81 | 82 | public final void error(String func, String msg, Throwable t) { 83 | logger.log(Level.ERROR, msg, t); 84 | } 85 | 86 | public final void finest(String func, String msg) { 87 | logger.log(Level.TRACE, msg); 88 | } 89 | 90 | public final void finest(String func, Throwable t) { 91 | logger.log(Level.TRACE, className + "::" + func, t); 92 | } 93 | 94 | public final void finest(String func, String msg, Throwable t) { 95 | logger.log(Level.TRACE, msg, t); 96 | } 97 | 98 | public final void finer(String func, String msg) { 99 | logger.log(Level.TRACE, msg); 100 | } 101 | 102 | public final void finer(String func, Throwable t) { 103 | logger.log(Level.TRACE, className + "::" + func, t); 104 | } 105 | 106 | public final void finer(String func, String msg, Throwable t) { 107 | logger.log(Level.DEBUG, msg, t); 108 | } 109 | 110 | public final void fine(String func, String msg) { 111 | logger.log(Level.DEBUG, msg); 112 | } 113 | 114 | public final void fine(String func, Throwable t) { 115 | logger.log(Level.DEBUG, className + "::" + func, t); 116 | } 117 | 118 | public final void fine(String func, String msg, Throwable t) { 119 | logger.log(Level.DEBUG, msg, t); 120 | } 121 | 122 | public final void config(String func, String msg) { 123 | logger.log(Level.DEBUG, msg); 124 | } 125 | 126 | public final void config(String func, Throwable t) { 127 | logger.log(Level.DEBUG, className + "::" + func, t); 128 | } 129 | 130 | public final void config(String func, String msg, Throwable t) { 131 | logger.log(Level.DEBUG, msg, t); 132 | } 133 | 134 | public final void info(String func, String msg) { 135 | logger.log(Level.INFO, msg); 136 | } 137 | 138 | public final void info(String func, Throwable t) { 139 | logger.log(Level.INFO, className + "::" + func, t); 140 | } 141 | 142 | public final void info(String func, String msg, Throwable t) { 143 | logger.log(Level.INFO, msg, t); 144 | } 145 | 146 | public final void warning(String func, String msg) { 147 | logger.log(Level.WARNING, msg); 148 | } 149 | 150 | public final void warning(String func, Throwable t) { 151 | logger.log(Level.WARNING, className + "::" + func, t); 152 | } 153 | 154 | public final void warning(String func, String msg, Throwable t) { 155 | logger.log(Level.WARNING, msg, t); 156 | } 157 | 158 | public final void severe(String func, String msg) { 159 | logger.log(Level.ERROR, msg); 160 | } 161 | 162 | public final void severe(String func, Throwable t) { 163 | logger.log(Level.ERROR, className + "::" + func, t); 164 | } 165 | 166 | public final void severe(String func, String msg, Throwable t) { 167 | logger.log(Level.ERROR, msg, t); 168 | } 169 | 170 | public final String getName() { 171 | return logger.getName(); 172 | } 173 | 174 | public final boolean isLoggable(Level level) { 175 | return logger.isLoggable(level); 176 | } 177 | 178 | public final void log(Level level, ResourceBundle bundle, String msg, 179 | Throwable thrown) { 180 | logger.log(level, bundle, msg, thrown); 181 | } 182 | 183 | public final void log(Level level, ResourceBundle bundle, String format, 184 | Object... params) { 185 | logger.log(level, bundle, format, params); 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /Implementor/basic/classes/standard/IIOException.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.classes.standard; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Copy of {@link javax.imageio.IIOException} 7 | */ 8 | public class IIOException extends IOException { 9 | private static final long serialVersionUID = -3216210718638985251L; 10 | 11 | /** 12 | * Constructs an {@code IIOException} with a given message 13 | * {@code String}. No underlying cause is set; 14 | * {@code getCause} will return {@code null}. 15 | * 16 | * @param message the error message. 17 | * 18 | * @see #getMessage 19 | */ 20 | public IIOException(String message) { 21 | super(message); 22 | } 23 | 24 | /** 25 | * Constructs an {@code IIOException} with a given message 26 | * {@code String} and a {@code Throwable} that was its 27 | * underlying cause. 28 | * 29 | * @param message the error message. 30 | * @param cause the {@code Throwable} ({@code Error} or 31 | * {@code Exception}) that caused this exception to occur. 32 | * 33 | * @see #getCause 34 | * @see #getMessage 35 | */ 36 | public IIOException(String message, Throwable cause) { 37 | super(message); 38 | initCause(cause); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Implementor/basic/classes/standard/IIOImage.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.classes.standard; 2 | 3 | import javax.imageio.metadata.IIOMetadata; 4 | import java.awt.image.BufferedImage; 5 | import java.awt.image.Raster; 6 | import java.awt.image.RenderedImage; 7 | import java.util.List; 8 | 9 | /** 10 | * Copy of {@link javax.imageio.IIOImage} 11 | */ 12 | public class IIOImage { 13 | 14 | /** 15 | * The {@code RenderedImage} being referenced. 16 | */ 17 | protected RenderedImage image; 18 | 19 | /** 20 | * The {@code Raster} being referenced. 21 | */ 22 | protected Raster raster; 23 | 24 | /** 25 | * A {@code List} of {@code BufferedImage} thumbnails, 26 | * or {@code null}. Non-{@code BufferedImage} objects 27 | * must not be stored in this {@code List}. 28 | */ 29 | protected List thumbnails = null; 30 | 31 | /** 32 | * An {@code IIOMetadata} object containing metadata 33 | * associated with the image. 34 | */ 35 | protected IIOMetadata metadata; 36 | 37 | /** 38 | * Constructs an {@code IIOImage} containing a 39 | * {@code RenderedImage}, and thumbnails and metadata 40 | * associated with it. 41 | * 42 | *

All parameters are stored by reference. 43 | * 44 | *

The {@code thumbnails} argument must either be 45 | * {@code null} or contain only {@code BufferedImage} 46 | * objects. 47 | * 48 | * @param image a {@code RenderedImage}. 49 | * @param thumbnails a {@code List} of {@code BufferedImage}s, 50 | * or {@code null}. 51 | * @param metadata an {@code IIOMetadata} object, or 52 | * {@code null}. 53 | * 54 | * @exception IllegalArgumentException if {@code image} is 55 | * {@code null}. 56 | */ 57 | public IIOImage(RenderedImage image, 58 | List thumbnails, 59 | IIOMetadata metadata) { 60 | if (image == null) { 61 | throw new IllegalArgumentException("image == null!"); 62 | } 63 | this.image = image; 64 | this.raster = null; 65 | this.thumbnails = thumbnails; 66 | this.metadata = metadata; 67 | } 68 | 69 | /** 70 | * Constructs an {@code IIOImage} containing a 71 | * {@code Raster}, and thumbnails and metadata 72 | * associated with it. 73 | * 74 | *

All parameters are stored by reference. 75 | * 76 | * @param raster a {@code Raster}. 77 | * @param thumbnails a {@code List} of {@code BufferedImage}s, 78 | * or {@code null}. 79 | * @param metadata an {@code IIOMetadata} object, or 80 | * {@code null}. 81 | * 82 | * @exception IllegalArgumentException if {@code raster} is 83 | * {@code null}. 84 | */ 85 | public IIOImage(Raster raster, 86 | List thumbnails, 87 | IIOMetadata metadata) { 88 | if (raster == null) { 89 | throw new IllegalArgumentException("raster == null!"); 90 | } 91 | this.raster = raster; 92 | this.image = null; 93 | this.thumbnails = thumbnails; 94 | this.metadata = metadata; 95 | } 96 | 97 | /** 98 | * Returns the currently set {@code RenderedImage}, or 99 | * {@code null} if only a {@code Raster} is available. 100 | * 101 | * @return a {@code RenderedImage}, or {@code null}. 102 | * 103 | * @see #setRenderedImage 104 | */ 105 | public RenderedImage getRenderedImage() { 106 | synchronized(this) { 107 | return image; 108 | } 109 | } 110 | 111 | /** 112 | * Sets the current {@code RenderedImage}. The value is 113 | * stored by reference. Any existing {@code Raster} is 114 | * discarded. 115 | * 116 | * @param image a {@code RenderedImage}. 117 | * 118 | * @exception IllegalArgumentException if {@code image} is 119 | * {@code null}. 120 | * 121 | * @see #getRenderedImage 122 | */ 123 | public void setRenderedImage(RenderedImage image) { 124 | synchronized(this) { 125 | if (image == null) { 126 | throw new IllegalArgumentException("image == null!"); 127 | } 128 | this.image = image; 129 | this.raster = null; 130 | } 131 | } 132 | 133 | /** 134 | * Returns {@code true} if this {@code IIOImage} stores 135 | * a {@code Raster} rather than a {@code RenderedImage}. 136 | * 137 | * @return {@code true} if a {@code Raster} is 138 | * available. 139 | */ 140 | public boolean hasRaster() { 141 | synchronized(this) { 142 | return (raster != null); 143 | } 144 | } 145 | 146 | /** 147 | * Returns the currently set {@code Raster}, or 148 | * {@code null} if only a {@code RenderedImage} is 149 | * available. 150 | * 151 | * @return a {@code Raster}, or {@code null}. 152 | * 153 | * @see #setRaster 154 | */ 155 | public Raster getRaster() { 156 | synchronized(this) { 157 | return raster; 158 | } 159 | } 160 | 161 | /** 162 | * Sets the current {@code Raster}. The value is 163 | * stored by reference. Any existing {@code RenderedImage} is 164 | * discarded. 165 | * 166 | * @param raster a {@code Raster}. 167 | * 168 | * @exception IllegalArgumentException if {@code raster} is 169 | * {@code null}. 170 | * 171 | * @see #getRaster 172 | */ 173 | public void setRaster(Raster raster) { 174 | synchronized(this) { 175 | if (raster == null) { 176 | throw new IllegalArgumentException("raster == null!"); 177 | } 178 | this.raster = raster; 179 | this.image = null; 180 | } 181 | } 182 | 183 | /** 184 | * Returns the number of thumbnails stored in this 185 | * {@code IIOImage}. 186 | * 187 | * @return the number of thumbnails, as an {@code int}. 188 | */ 189 | public int getNumThumbnails() { 190 | return thumbnails == null ? 0 : thumbnails.size(); 191 | } 192 | 193 | /** 194 | * Returns a thumbnail associated with the main image. 195 | * 196 | * @param index the index of the desired thumbnail image. 197 | * 198 | * @return a thumbnail image, as a {@code BufferedImage}. 199 | * 200 | * @exception IndexOutOfBoundsException if the supplied index is 201 | * negative or larger than the largest valid index. 202 | * @exception ClassCastException if a 203 | * non-{@code BufferedImage} object is encountered in the 204 | * list of thumbnails at the given index. 205 | * 206 | * @see #getThumbnails 207 | * @see #setThumbnails 208 | */ 209 | public BufferedImage getThumbnail(int index) { 210 | if (thumbnails == null) { 211 | throw new IndexOutOfBoundsException("No thumbnails available!"); 212 | } 213 | return (BufferedImage)thumbnails.get(index); 214 | } 215 | 216 | /** 217 | * Returns the current {@code List} of thumbnail 218 | * {@code BufferedImage}s, or {@code null} if none is 219 | * set. A live reference is returned. 220 | * 221 | * @return the current {@code List} of 222 | * {@code BufferedImage} thumbnails, or {@code null}. 223 | * 224 | * @see #getThumbnail(int) 225 | * @see #setThumbnails 226 | */ 227 | public List getThumbnails() { 228 | return thumbnails; 229 | } 230 | 231 | /** 232 | * Sets the list of thumbnails to a new {@code List} of 233 | * {@code BufferedImage}s, or to {@code null}. The 234 | * reference to the previous {@code List} is discarded. 235 | * 236 | *

The {@code thumbnails} argument must either be 237 | * {@code null} or contain only {@code BufferedImage} 238 | * objects. 239 | * 240 | * @param thumbnails a {@code List} of 241 | * {@code BufferedImage} thumbnails, or {@code null}. 242 | * 243 | * @see #getThumbnail(int) 244 | * @see #getThumbnails 245 | */ 246 | public void setThumbnails(List thumbnails) { 247 | this.thumbnails = thumbnails; 248 | } 249 | 250 | /** 251 | * Returns a reference to the current {@code IIOMetadata} 252 | * object, or {@code null} is none is set. 253 | * 254 | * @return an {@code IIOMetadata} object, or {@code null}. 255 | * 256 | * @see #setMetadata 257 | */ 258 | public IIOMetadata getMetadata() { 259 | return metadata; 260 | } 261 | 262 | /** 263 | * Sets the {@code IIOMetadata} to a new object, or 264 | * {@code null}. 265 | * 266 | * @param metadata an {@code IIOMetadata} object, or 267 | * {@code null}. 268 | * 269 | * @see #getMetadata 270 | */ 271 | public void setMetadata(IIOMetadata metadata) { 272 | this.metadata = metadata; 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /Implementor/basic/classes/standard/Logger.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.classes.standard; 2 | 3 | import java.util.Objects; 4 | import java.util.ResourceBundle; 5 | import java.util.function.Supplier; 6 | 7 | /** 8 | * Copy of {@link java.lang.System.Logger} 9 | */ 10 | public interface Logger { 11 | 12 | /** 13 | * System {@linkplain System.Logger loggers} levels. 14 | *

15 | * A level has a {@linkplain #getName() name} and {@linkplain 16 | * #getSeverity() severity}. 17 | * Level values are {@link #ALL}, {@link #TRACE}, {@link #DEBUG}, 18 | * {@link #INFO}, {@link #WARNING}, {@link #ERROR}, {@link #OFF}, 19 | * by order of increasing severity. 20 | *
21 | * {@link #ALL} and {@link #OFF} 22 | * are simple markers with severities mapped respectively to 23 | * {@link java.lang.Integer#MIN_VALUE Integer.MIN_VALUE} and 24 | * {@link java.lang.Integer#MAX_VALUE Integer.MAX_VALUE}. 25 | *

26 | * Severity values and Mapping to {@code java.util.logging.Level}. 27 | *

28 | * {@linkplain Level System logger levels} are mapped to 29 | * {@linkplain java.util.logging.Level java.util.logging levels} 30 | * of corresponding severity. 31 | *
The mapping is as follows: 32 | *

33 | * 34 | * 35 | * 36 | * 37 | * 38 | * 39 | * 40 | * 41 | * 42 | * 43 | * 44 | * 45 | * 46 | * 47 | * 48 | * 49 | * 50 | * 51 | * 52 | * 53 | *
System.Logger Severity Level Mapping
System.Logger Levels{@link Level#ALL ALL}{@link Level#TRACE TRACE}{@link Level#DEBUG DEBUG}{@link Level#INFO INFO}{@link Level#WARNING WARNING}{@link Level#ERROR ERROR}{@link Level#OFF OFF}
java.util.logging Levels{@link java.util.logging.Level#ALL ALL}{@link java.util.logging.Level#FINER FINER}{@link java.util.logging.Level#FINE FINE}{@link java.util.logging.Level#INFO INFO}{@link java.util.logging.Level#WARNING WARNING}{@link java.util.logging.Level#SEVERE SEVERE}{@link java.util.logging.Level#OFF OFF}
54 | * 55 | * @since 9 56 | * 57 | * @see java.lang.System.LoggerFinder 58 | * @see java.lang.System.Logger 59 | */ 60 | public enum Level { 61 | 62 | // for convenience, we're reusing java.util.logging.Level int values 63 | // the mapping logic in sun.util.logging.PlatformLogger depends 64 | // on this. 65 | /** 66 | * A marker to indicate that all levels are enabled. 67 | * This level {@linkplain #getSeverity() severity} is 68 | * {@link Integer#MIN_VALUE}. 69 | */ 70 | ALL(Integer.MIN_VALUE), // typically mapped to/from j.u.l.Level.ALL 71 | /** 72 | * {@code TRACE} level: usually used to log diagnostic information. 73 | * This level {@linkplain #getSeverity() severity} is 74 | * {@code 400}. 75 | */ 76 | TRACE(400), // typically mapped to/from j.u.l.Level.FINER 77 | /** 78 | * {@code DEBUG} level: usually used to log debug information traces. 79 | * This level {@linkplain #getSeverity() severity} is 80 | * {@code 500}. 81 | */ 82 | DEBUG(500), // typically mapped to/from j.u.l.Level.FINEST/FINE/CONFIG 83 | /** 84 | * {@code INFO} level: usually used to log information messages. 85 | * This level {@linkplain #getSeverity() severity} is 86 | * {@code 800}. 87 | */ 88 | INFO(800), // typically mapped to/from j.u.l.Level.INFO 89 | /** 90 | * {@code WARNING} level: usually used to log warning messages. 91 | * This level {@linkplain #getSeverity() severity} is 92 | * {@code 900}. 93 | */ 94 | WARNING(900), // typically mapped to/from j.u.l.Level.WARNING 95 | /** 96 | * {@code ERROR} level: usually used to log error messages. 97 | * This level {@linkplain #getSeverity() severity} is 98 | * {@code 1000}. 99 | */ 100 | ERROR(1000), // typically mapped to/from j.u.l.Level.SEVERE 101 | /** 102 | * A marker to indicate that all levels are disabled. 103 | * This level {@linkplain #getSeverity() severity} is 104 | * {@link Integer#MAX_VALUE}. 105 | */ 106 | OFF(Integer.MAX_VALUE); // typically mapped to/from j.u.l.Level.OFF 107 | 108 | private final int severity; 109 | 110 | private Level(int severity) { 111 | this.severity = severity; 112 | } 113 | 114 | /** 115 | * Returns the name of this level. 116 | * @return this level {@linkplain #name()}. 117 | */ 118 | public final String getName() { 119 | return name(); 120 | } 121 | 122 | /** 123 | * Returns the severity of this level. 124 | * A higher severity means a more severe condition. 125 | * @return this level severity. 126 | */ 127 | public final int getSeverity() { 128 | return severity; 129 | } 130 | } 131 | 132 | /** 133 | * Returns the name of this logger. 134 | * 135 | * @return the logger name. 136 | */ 137 | public String getName(); 138 | 139 | /** 140 | * Checks if a message of the given level would be logged by 141 | * this logger. 142 | * 143 | * @param level the log message level. 144 | * @return {@code true} if the given log message level is currently 145 | * being logged. 146 | * 147 | * @throws NullPointerException if {@code level} is {@code null}. 148 | */ 149 | public boolean isLoggable(Level level); 150 | 151 | /** 152 | * Logs a message. 153 | * 154 | * @implSpec The default implementation for this method calls 155 | * {@code this.log(level, (ResourceBundle)null, msg, (Object[])null);} 156 | * 157 | * @param level the log message level. 158 | * @param msg the string message (or a key in the message catalog, if 159 | * this logger is a {@link 160 | * LoggerFinder#getLocalizedLogger(java.lang.String, 161 | * java.util.ResourceBundle, java.lang.Module) localized logger}); 162 | * can be {@code null}. 163 | * 164 | * @throws NullPointerException if {@code level} is {@code null}. 165 | */ 166 | public default void log(Level level, String msg) { 167 | log(level, (ResourceBundle) null, msg, (Object[]) null); 168 | } 169 | 170 | /** 171 | * Logs a lazily supplied message. 172 | *

173 | * If the logger is currently enabled for the given log message level 174 | * then a message is logged that is the result produced by the 175 | * given supplier function. Otherwise, the supplier is not operated on. 176 | * 177 | * @implSpec When logging is enabled for the given level, the default 178 | * implementation for this method calls 179 | * {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), (Object[])null);} 180 | * 181 | * @param level the log message level. 182 | * @param msgSupplier a supplier function that produces a message. 183 | * 184 | * @throws NullPointerException if {@code level} is {@code null}, 185 | * or {@code msgSupplier} is {@code null}. 186 | */ 187 | public default void log(Level level, Supplier msgSupplier) { 188 | Objects.requireNonNull(msgSupplier); 189 | if (isLoggable(Objects.requireNonNull(level))) { 190 | log(level, (ResourceBundle) null, msgSupplier.get(), (Object[]) null); 191 | } 192 | } 193 | 194 | /** 195 | * Logs a message produced from the given object. 196 | *

197 | * If the logger is currently enabled for the given log message level then 198 | * a message is logged that, by default, is the result produced from 199 | * calling toString on the given object. 200 | * Otherwise, the object is not operated on. 201 | * 202 | * @implSpec When logging is enabled for the given level, the default 203 | * implementation for this method calls 204 | * {@code this.log(level, (ResourceBundle)null, obj.toString(), (Object[])null);} 205 | * 206 | * @param level the log message level. 207 | * @param obj the object to log. 208 | * 209 | * @throws NullPointerException if {@code level} is {@code null}, or 210 | * {@code obj} is {@code null}. 211 | */ 212 | public default void log(Level level, Object obj) { 213 | Objects.requireNonNull(obj); 214 | if (isLoggable(Objects.requireNonNull(level))) { 215 | this.log(level, (ResourceBundle) null, obj.toString(), (Object[]) null); 216 | } 217 | } 218 | 219 | /** 220 | * Logs a message associated with a given throwable. 221 | * 222 | * @implSpec The default implementation for this method calls 223 | * {@code this.log(level, (ResourceBundle)null, msg, thrown);} 224 | * 225 | * @param level the log message level. 226 | * @param msg the string message (or a key in the message catalog, if 227 | * this logger is a {@link 228 | * LoggerFinder#getLocalizedLogger(java.lang.String, 229 | * java.util.ResourceBundle, java.lang.Module) localized logger}); 230 | * can be {@code null}. 231 | * @param thrown a {@code Throwable} associated with the log message; 232 | * can be {@code null}. 233 | * 234 | * @throws NullPointerException if {@code level} is {@code null}. 235 | */ 236 | public default void log(Level level, String msg, Throwable thrown) { 237 | this.log(level, null, msg, thrown); 238 | } 239 | 240 | /** 241 | * Logs a lazily supplied message associated with a given throwable. 242 | *

243 | * If the logger is currently enabled for the given log message level 244 | * then a message is logged that is the result produced by the 245 | * given supplier function. Otherwise, the supplier is not operated on. 246 | * 247 | * @implSpec When logging is enabled for the given level, the default 248 | * implementation for this method calls 249 | * {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), thrown);} 250 | * 251 | * @param level one of the log message level identifiers. 252 | * @param msgSupplier a supplier function that produces a message. 253 | * @param thrown a {@code Throwable} associated with log message; 254 | * can be {@code null}. 255 | * 256 | * @throws NullPointerException if {@code level} is {@code null}, or 257 | * {@code msgSupplier} is {@code null}. 258 | */ 259 | public default void log(Level level, Supplier msgSupplier, 260 | Throwable thrown) { 261 | Objects.requireNonNull(msgSupplier); 262 | if (isLoggable(Objects.requireNonNull(level))) { 263 | this.log(level, null, msgSupplier.get(), thrown); 264 | } 265 | } 266 | 267 | /** 268 | * Logs a message with an optional list of parameters. 269 | * 270 | * @implSpec The default implementation for this method calls 271 | * {@code this.log(level, (ResourceBundle)null, format, params);} 272 | * 273 | * @param level one of the log message level identifiers. 274 | * @param format the string message format in {@link 275 | * java.text.MessageFormat} format, (or a key in the message 276 | * catalog, if this logger is a {@link 277 | * LoggerFinder#getLocalizedLogger(java.lang.String, 278 | * java.util.ResourceBundle, java.lang.Module) localized logger}); 279 | * can be {@code null}. 280 | * @param params an optional list of parameters to the message (may be 281 | * none). 282 | * 283 | * @throws NullPointerException if {@code level} is {@code null}. 284 | */ 285 | public default void log(Level level, String format, Object... params) { 286 | this.log(level, null, format, params); 287 | } 288 | 289 | /** 290 | * Logs a localized message associated with a given throwable. 291 | *

292 | * If the given resource bundle is non-{@code null}, the {@code msg} 293 | * string is localized using the given resource bundle. 294 | * Otherwise the {@code msg} string is not localized. 295 | * 296 | * @param level the log message level. 297 | * @param bundle a resource bundle to localize {@code msg}; can be 298 | * {@code null}. 299 | * @param msg the string message (or a key in the message catalog, 300 | * if {@code bundle} is not {@code null}); can be {@code null}. 301 | * @param thrown a {@code Throwable} associated with the log message; 302 | * can be {@code null}. 303 | * 304 | * @throws NullPointerException if {@code level} is {@code null}. 305 | */ 306 | public void log(Level level, ResourceBundle bundle, String msg, 307 | Throwable thrown); 308 | 309 | /** 310 | * Logs a message with resource bundle and an optional list of 311 | * parameters. 312 | *

313 | * If the given resource bundle is non-{@code null}, the {@code format} 314 | * string is localized using the given resource bundle. 315 | * Otherwise the {@code format} string is not localized. 316 | * 317 | * @param level the log message level. 318 | * @param bundle a resource bundle to localize {@code format}; can be 319 | * {@code null}. 320 | * @param format the string message format in {@link 321 | * java.text.MessageFormat} format, (or a key in the message 322 | * catalog if {@code bundle} is not {@code null}); can be {@code null}. 323 | * @param params an optional list of parameters to the message (may be 324 | * none). 325 | * 326 | * @throws NullPointerException if {@code level} is {@code null}. 327 | */ 328 | public void log(Level level, ResourceBundle bundle, String format, 329 | Object... params); 330 | 331 | } 332 | -------------------------------------------------------------------------------- /Implementor/basic/classes/standard/RMIServerImpl.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.classes.standard; 2 | 3 | import javax.management.MBeanServer; 4 | import javax.management.remote.JMXAuthenticator; 5 | import javax.management.remote.JMXConnectorServer; 6 | import javax.management.remote.rmi.RMIConnection; 7 | import javax.management.remote.rmi.RMIConnectorServer; 8 | import javax.management.remote.rmi.RMIServer; 9 | import javax.security.auth.Subject; 10 | import java.io.Closeable; 11 | import java.io.IOException; 12 | import java.lang.ref.WeakReference; 13 | import java.rmi.Remote; 14 | import java.rmi.server.RemoteServer; 15 | import java.rmi.server.ServerNotActiveException; 16 | import java.security.Principal; 17 | import java.util.*; 18 | 19 | /** 20 | * Copy of {@link javax.management.remote.rmi.RMIServerImpl} 21 | */ 22 | public abstract class RMIServerImpl implements Closeable, RMIServer { 23 | /** 24 | *

Constructs a new RMIServerImpl.

25 | * 26 | * @param env the environment containing attributes for the new 27 | * RMIServerImpl. Can be null, which is equivalent 28 | * to an empty Map. 29 | */ 30 | public RMIServerImpl(Map env) { 31 | this.env = (env == null) ? Collections.emptyMap() : env; 32 | } 33 | 34 | void setRMIConnectorServer(RMIConnectorServer connServer) 35 | throws IOException { 36 | this.connServer = connServer; 37 | } 38 | 39 | /** 40 | *

Exports this RMI object.

41 | * 42 | * @exception IOException if this RMI object cannot be exported. 43 | */ 44 | protected abstract void export() throws IOException; 45 | 46 | /** 47 | * Returns a remotable stub for this server object. 48 | * @return a remotable stub. 49 | * @exception IOException if the stub cannot be obtained - e.g the 50 | * RMIServerImpl has not been exported yet. 51 | **/ 52 | public abstract Remote toStub() throws IOException; 53 | 54 | /** 55 | *

Sets the default ClassLoader for this connector 56 | * server. New client connections will use this classloader. 57 | * Existing client connections are unaffected.

58 | * 59 | * @param cl the new ClassLoader to be used by this 60 | * connector server. 61 | * 62 | * @see #getDefaultClassLoader 63 | */ 64 | public synchronized void setDefaultClassLoader(ClassLoader cl) { 65 | this.cl = cl; 66 | } 67 | 68 | /** 69 | *

Gets the default ClassLoader used by this connector 70 | * server.

71 | * 72 | * @return the default ClassLoader used by this 73 | * connector server. 74 | * 75 | * @see #setDefaultClassLoader 76 | */ 77 | public synchronized ClassLoader getDefaultClassLoader() { 78 | return cl; 79 | } 80 | 81 | /** 82 | *

Sets the MBeanServer to which this connector 83 | * server is attached. New client connections will interact 84 | * with this MBeanServer. Existing client connections are 85 | * unaffected.

86 | * 87 | * @param mbs the new MBeanServer. Can be null, but 88 | * new client connections will be refused as long as it is. 89 | * 90 | * @see #getMBeanServer 91 | */ 92 | public synchronized void setMBeanServer(MBeanServer mbs) { 93 | this.mbeanServer = mbs; 94 | } 95 | 96 | /** 97 | *

The MBeanServer to which this connector server 98 | * is attached. This is the last value passed to {@link 99 | * #setMBeanServer} on this object, or null if that method has 100 | * never been called.

101 | * 102 | * @return the MBeanServer to which this connector 103 | * is attached. 104 | * 105 | * @see #setMBeanServer 106 | */ 107 | public synchronized MBeanServer getMBeanServer() { 108 | return mbeanServer; 109 | } 110 | 111 | public String getVersion() { 112 | // Expected format is: "protocol-version implementation-name" 113 | try { 114 | return "1.0 java_runtime_" + 115 | System.getProperty("java.runtime.version"); 116 | } catch (SecurityException e) { 117 | return "1.0 "; 118 | } 119 | } 120 | 121 | /** 122 | *

Creates a new client connection. This method calls {@link 123 | * #makeClient makeClient} and adds the returned client connection 124 | * object to an internal list. When this 125 | * RMIServerImpl is shut down via its {@link 126 | * #close()} method, the {@link RMIConnection#close() close()} 127 | * method of each object remaining in the list is called.

128 | * 129 | *

The fact that a client connection object is in this internal 130 | * list does not prevent it from being garbage collected.

131 | * 132 | * @param credentials this object specifies the user-defined 133 | * credentials to be passed in to the server in order to 134 | * authenticate the caller before creating the 135 | * RMIConnection. Can be null. 136 | * 137 | * @return the newly-created RMIConnection. This is 138 | * usually the object created by makeClient, though 139 | * an implementation may choose to wrap that object in another 140 | * object implementing RMIConnection. 141 | * 142 | * @exception IOException if the new client object cannot be 143 | * created or exported. 144 | * 145 | * @exception SecurityException if the given credentials do not allow 146 | * the server to authenticate the user successfully. 147 | * 148 | * @exception IllegalStateException if {@link #getMBeanServer()} 149 | * is null. 150 | */ 151 | public RMIConnection newClient(Object credentials) throws IOException { 152 | return doNewClient(credentials); 153 | } 154 | 155 | /** 156 | * This method could be overridden by subclasses defined in this package 157 | * to perform additional operations specific to the underlying transport 158 | * before creating the new client connection. 159 | */ 160 | RMIConnection doNewClient(Object credentials) throws IOException { 161 | final boolean tracing = logger.traceOn(); 162 | 163 | if (tracing) logger.trace("newClient","making new client"); 164 | 165 | if (getMBeanServer() == null) 166 | throw new IllegalStateException("Not attached to an MBean server"); 167 | 168 | Subject subject = null; 169 | JMXAuthenticator authenticator = 170 | (JMXAuthenticator) env.get(JMXConnectorServer.AUTHENTICATOR); 171 | if (authenticator == null) { 172 | /* 173 | * Create the JAAS-based authenticator only if authentication 174 | * has been enabled 175 | */ 176 | if (env.get("jmx.remote.x.password.file") != null || 177 | env.get("jmx.remote.x.login.config") != null) { 178 | } 179 | } 180 | if (authenticator != null) { 181 | if (tracing) logger.trace("newClient","got authenticator: " + 182 | authenticator.getClass().getName()); 183 | try { 184 | subject = authenticator.authenticate(credentials); 185 | } catch (SecurityException e) { 186 | logger.trace("newClient", "Authentication failed: " + e); 187 | throw e; 188 | } 189 | } 190 | 191 | if (tracing) { 192 | if (subject != null) 193 | logger.trace("newClient","subject is not null"); 194 | else logger.trace("newClient","no subject"); 195 | } 196 | 197 | final String connectionId = makeConnectionId(getProtocol(), subject); 198 | 199 | if (tracing) 200 | logger.trace("newClient","making new connection: " + connectionId); 201 | 202 | RMIConnection client = makeClient(connectionId, subject); 203 | 204 | dropDeadReferences(); 205 | WeakReference wr = new WeakReference(client); 206 | synchronized (clientList) { 207 | clientList.add(wr); 208 | } 209 | 210 | synchronized (clientList) { 211 | if (!clientList.contains(wr)) { 212 | // can be removed only by a JMXConnectionNotification listener 213 | throw new IOException("The connection is refused."); 214 | } 215 | } 216 | 217 | if (tracing) 218 | logger.trace("newClient","new connection done: " + connectionId ); 219 | 220 | return client; 221 | } 222 | 223 | /** 224 | *

Creates a new client connection. This method is called by 225 | * the public method {@link #newClient(Object)}.

226 | * 227 | * @param connectionId the ID of the new connection. Every 228 | * connection opened by this connector server will have a 229 | * different ID. The behavior is unspecified if this parameter is 230 | * null. 231 | * 232 | * @param subject the authenticated subject. Can be null. 233 | * 234 | * @return the newly-created RMIConnection. 235 | * 236 | * @exception IOException if the new client object cannot be 237 | * created or exported. 238 | */ 239 | protected abstract RMIConnection makeClient(String connectionId, 240 | Subject subject) 241 | throws IOException; 242 | 243 | /** 244 | *

Closes a client connection made by {@link #makeClient makeClient}. 245 | * 246 | * @param client a connection previously returned by 247 | * makeClient on which the closeClient 248 | * method has not previously been called. The behavior is 249 | * unspecified if these conditions are violated, including the 250 | * case where client is null. 251 | * 252 | * @exception IOException if the client connection cannot be 253 | * closed. 254 | */ 255 | protected abstract void closeClient(RMIConnection client) 256 | throws IOException; 257 | 258 | /** 259 | *

Returns the protocol string for this object. The string is 260 | * rmi for RMI/JRMP and iiop for RMI/IIOP. 261 | * 262 | * @return the protocol string for this object. 263 | */ 264 | protected abstract String getProtocol(); 265 | 266 | /** 267 | *

Method called when a client connection created by {@link 268 | * #makeClient makeClient} is closed. A subclass that defines 269 | * makeClient must arrange for this method to be 270 | * called when the resultant object's {@link RMIConnection#close() 271 | * close} method is called. This enables it to be removed from 272 | * the RMIServerImpl's list of connections. It is 273 | * not an error for client not to be in that 274 | * list.

275 | * 276 | *

After removing client from the list of 277 | * connections, this method calls {@link #closeClient 278 | * closeClient(client)}.

279 | * 280 | * @param client the client connection that has been closed. 281 | * 282 | * @exception IOException if {@link #closeClient} throws this 283 | * exception. 284 | * 285 | * @exception NullPointerException if client is null. 286 | */ 287 | protected void clientClosed(RMIConnection client) throws IOException { 288 | final boolean debug = logger.debugOn(); 289 | 290 | if (debug) logger.trace("clientClosed","client="+client); 291 | 292 | if (client == null) 293 | throw new NullPointerException("Null client"); 294 | 295 | synchronized (clientList) { 296 | dropDeadReferences(); 297 | for (Iterator> it = clientList.iterator(); 298 | it.hasNext(); ) { 299 | WeakReference wr = it.next(); 300 | if (wr.get() == client) { 301 | it.remove(); 302 | break; 303 | } 304 | } 305 | /* It is not a bug for this loop not to find the client. In 306 | our close() method, we remove a client from the list before 307 | calling its close() method. */ 308 | } 309 | 310 | if (debug) logger.trace("clientClosed", "closing client."); 311 | closeClient(client); 312 | 313 | if (debug) logger.trace("clientClosed", "sending notif"); 314 | 315 | if (debug) logger.trace("clientClosed","done"); 316 | } 317 | 318 | /** 319 | *

Closes this connection server. This method first calls the 320 | * {@link #closeServer()} method so that no new client connections 321 | * will be accepted. Then, for each remaining {@link 322 | * RMIConnection} object returned by {@link #makeClient 323 | * makeClient}, its {@link RMIConnection#close() close} method is 324 | * called.

325 | * 326 | *

The behavior when this method is called more than once is 327 | * unspecified.

328 | * 329 | *

If {@link #closeServer()} throws an 330 | * IOException, the individual connections are 331 | * nevertheless closed, and then the IOException is 332 | * thrown from this method.

333 | * 334 | *

If {@link #closeServer()} returns normally but one or more 335 | * of the individual connections throws an 336 | * IOException, then, after closing all the 337 | * connections, one of those IOExceptions is thrown 338 | * from this method. If more than one connection throws an 339 | * IOException, it is unspecified which one is thrown 340 | * from this method.

341 | * 342 | * @exception IOException if {@link #closeServer()} or one of the 343 | * {@link RMIConnection#close()} calls threw 344 | * IOException. 345 | */ 346 | public synchronized void close() throws IOException { 347 | final boolean tracing = logger.traceOn(); 348 | final boolean debug = logger.debugOn(); 349 | 350 | if (tracing) logger.trace("close","closing"); 351 | 352 | IOException ioException = null; 353 | try { 354 | if (debug) logger.debug("close","closing Server"); 355 | closeServer(); 356 | } catch (IOException e) { 357 | if (tracing) logger.trace("close","Failed to close server: " + e); 358 | if (debug) logger.debug("close",e); 359 | ioException = e; 360 | } 361 | 362 | if (debug) logger.debug("close","closing Clients"); 363 | // Loop to close all clients 364 | while (true) { 365 | synchronized (clientList) { 366 | if (debug) logger.debug("close","droping dead references"); 367 | dropDeadReferences(); 368 | 369 | if (debug) logger.debug("close","client count: "+clientList.size()); 370 | if (clientList.size() == 0) 371 | break; 372 | /* Loop until we find a non-null client. Because we called 373 | dropDeadReferences(), this will usually be the first 374 | element of the list, but a garbage collection could have 375 | happened in between. */ 376 | for (Iterator> it = clientList.iterator(); 377 | it.hasNext(); ) { 378 | WeakReference wr = it.next(); 379 | RMIConnection client = wr.get(); 380 | it.remove(); 381 | if (client != null) { 382 | try { 383 | client.close(); 384 | } catch (IOException e) { 385 | if (tracing) 386 | logger.trace("close","Failed to close client: " + e); 387 | if (debug) logger.debug("close",e); 388 | if (ioException == null) 389 | ioException = e; 390 | } 391 | break; 392 | } 393 | } 394 | } 395 | } 396 | 397 | if (ioException != null) { 398 | if (tracing) logger.trace("close","close failed."); 399 | throw ioException; 400 | } 401 | 402 | if (tracing) logger.trace("close","closed."); 403 | } 404 | 405 | /** 406 | *

Called by {@link #close()} to close the connector server. 407 | * After returning from this method, the connector server must 408 | * not accept any new connections.

409 | * 410 | * @exception IOException if the attempt to close the connector 411 | * server failed. 412 | */ 413 | protected abstract void closeServer() throws IOException; 414 | 415 | private static synchronized String makeConnectionId(String protocol, 416 | Subject subject) { 417 | connectionIdNumber++; 418 | 419 | String clientHost = ""; 420 | try { 421 | clientHost = RemoteServer.getClientHost(); 422 | /* 423 | * According to the rules specified in the javax.management.remote 424 | * package description, a numeric IPv6 address (detected by the 425 | * presence of otherwise forbidden ":" character) forming a part 426 | * of the connection id must be enclosed in square brackets. 427 | */ 428 | if (clientHost.contains(":")) { 429 | clientHost = "[" + clientHost + "]"; 430 | } 431 | } catch (ServerNotActiveException e) { 432 | logger.trace("makeConnectionId", "getClientHost", e); 433 | } 434 | 435 | final StringBuilder buf = new StringBuilder(); 436 | buf.append(protocol).append(":"); 437 | if (clientHost.length() > 0) 438 | buf.append("//").append(clientHost); 439 | buf.append(" "); 440 | if (subject != null) { 441 | Set principals = subject.getPrincipals(); 442 | String sep = ""; 443 | for (Iterator it = principals.iterator(); it.hasNext(); ) { 444 | Principal p = it.next(); 445 | String name = p.getName().replace(' ', '_').replace(';', ':'); 446 | buf.append(sep).append(name); 447 | sep = ";"; 448 | } 449 | } 450 | buf.append(" ").append(connectionIdNumber); 451 | if (logger.traceOn()) 452 | logger.trace("newConnectionId","connectionId="+buf); 453 | return buf.toString(); 454 | } 455 | 456 | private void dropDeadReferences() { 457 | synchronized (clientList) { 458 | for (Iterator> it = clientList.iterator(); 459 | it.hasNext(); ) { 460 | WeakReference wr = it.next(); 461 | if (wr.get() == null) 462 | it.remove(); 463 | } 464 | } 465 | } 466 | 467 | private static final ClassLogger logger = 468 | new ClassLogger("javax.management.remote.rmi", "RMIServerImpl"); 469 | 470 | /** List of WeakReference values. Each one references an 471 | RMIConnection created by this object, or null if the 472 | RMIConnection has been garbage-collected. */ 473 | private final List> clientList = 474 | new ArrayList>(); 475 | 476 | private ClassLoader cl; 477 | 478 | private MBeanServer mbeanServer; 479 | 480 | private final Map env; 481 | 482 | private RMIConnectorServer connServer; 483 | 484 | private static int connectionIdNumber; 485 | } 486 | -------------------------------------------------------------------------------- /Implementor/basic/classes/standard/RelationNotFoundException.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.classes.standard; 2 | 3 | import javax.management.relation.RelationException; 4 | 5 | /** 6 | * Copy of {@link javax.management.relation.RelationNotFoundException} 7 | */ 8 | public class RelationNotFoundException extends RelationException { 9 | 10 | /* Serial version */ 11 | private static final long serialVersionUID = -3793951411158559116L; 12 | 13 | /** 14 | * Constructor with given message put in exception. 15 | * 16 | * @param message the detail message. 17 | */ 18 | public RelationNotFoundException(String message) { 19 | super(message); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Implementor/basic/classes/standard/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Standard classes for basic tests 3 | * of Implementor homework 4 | * for Java Advanced course. 5 | * 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | package info.kgeorgiy.java.advanced.implementor.basic.classes.standard; -------------------------------------------------------------------------------- /Implementor/basic/interfaces/InterfaceWithDefaultMethod.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.interfaces; 2 | 3 | /** 4 | * Basic interface basic for easy version 5 | * of Implementor homework 6 | * for Java Advanced course. 7 | * 8 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 9 | */ 10 | public interface InterfaceWithDefaultMethod { 11 | int hello(); 12 | default void defaultMethod() { 13 | System.out.println("defaultMethod"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Implementor/basic/interfaces/InterfaceWithStaticMethod.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.interfaces; 2 | 3 | /** 4 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 5 | */ 6 | public interface InterfaceWithStaticMethod { 7 | int hello(); 8 | static void staticMethod() { 9 | System.out.println("staticMethod"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Implementor/basic/interfaces/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Hand-made interfaces for basic tests 3 | * of Implementor homework 4 | * for Java Advanced course. 5 | * 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | package info.kgeorgiy.java.advanced.implementor.basic.interfaces; -------------------------------------------------------------------------------- /Implementor/basic/interfaces/standard/Accessible.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.interfaces.standard; 2 | 3 | import javax.accessibility.AccessibleContext; 4 | 5 | /** 6 | * Copy of {@link javax.accessibility.Accessible} 7 | */ 8 | public interface Accessible { 9 | 10 | /** 11 | * Returns the {@code AccessibleContext} associated with this object. In 12 | * most cases, the return value should not be {@code null} if the object 13 | * implements interface {@code Accessible}. If a component developer creates 14 | * a subclass of an object that implements {@code Accessible}, and that 15 | * subclass is not {@code Accessible}, the developer should override the 16 | * {@code getAccessibleContext} method to return {@code null}. 17 | * 18 | * @return the {@code AccessibleContext} associated with this object 19 | */ 20 | public AccessibleContext getAccessibleContext(); 21 | } 22 | -------------------------------------------------------------------------------- /Implementor/basic/interfaces/standard/Descriptor.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.interfaces.standard; 2 | 3 | import javax.management.RuntimeOperationsException; 4 | import java.io.Serializable; 5 | import java.util.Arrays; 6 | 7 | /** 8 | * Copy of {@link javax.management.Descriptor} 9 | */ 10 | public interface Descriptor extends Serializable, Cloneable 11 | { 12 | 13 | /** 14 | * Returns the value for a specific field name, or null if no value 15 | * is present for that name. 16 | * 17 | * @param fieldName the field name. 18 | * 19 | * @return the corresponding value, or null if the field is not present. 20 | * 21 | * @exception RuntimeOperationsException if the field name is illegal. 22 | */ 23 | public Object getFieldValue(String fieldName) 24 | throws RuntimeOperationsException; 25 | 26 | /** 27 | *

Sets the value for a specific field name. This will 28 | * modify an existing field or add a new field.

29 | * 30 | *

The field value will be validated before it is set. 31 | * If it is not valid, then an exception will be thrown. 32 | * The meaning of validity is dependent on the descriptor 33 | * implementation.

34 | * 35 | * @param fieldName The field name to be set. Cannot be null or empty. 36 | * @param fieldValue The field value to be set for the field 37 | * name. Can be null if that is a valid value for the field. 38 | * 39 | * @exception RuntimeOperationsException if the field name or field value 40 | * is illegal (wrapped exception is {@link IllegalArgumentException}); or 41 | * if the descriptor is immutable (wrapped exception is 42 | * {@link UnsupportedOperationException}). 43 | */ 44 | public void setField(String fieldName, Object fieldValue) 45 | throws RuntimeOperationsException; 46 | 47 | 48 | /** 49 | * Returns all of the fields contained in this descriptor as a string array. 50 | * 51 | * @return String array of fields in the format fieldName=fieldValue 52 | *
If the value of a field is not a String, then the toString() method 53 | * will be called on it and the returned value, enclosed in parentheses, 54 | * used as the value for the field in the returned array. If the value 55 | * of a field is null, then the value of the field in the returned array 56 | * will be empty. If the descriptor is empty, you will get 57 | * an empty array. 58 | * 59 | * @see #setFields 60 | */ 61 | public String[] getFields(); 62 | 63 | 64 | /** 65 | * Returns all the field names in the descriptor. 66 | * 67 | * @return String array of field names. If the descriptor is empty, 68 | * you will get an empty array. 69 | */ 70 | public String[] getFieldNames(); 71 | 72 | /** 73 | * Returns all the field values in the descriptor as an array of Objects. The 74 | * returned values are in the same order as the {@code fieldNames} String array parameter. 75 | * 76 | * @param fieldNames String array of the names of the fields that 77 | * the values should be returned for. If the array is empty then 78 | * an empty array will be returned. If the array is null then all 79 | * values will be returned, as if the parameter were the array 80 | * returned by {@link #getFieldNames()}. If a field name in the 81 | * array does not exist, including the case where it is null or 82 | * the empty string, then null is returned for the matching array 83 | * element being returned. 84 | * 85 | * @return Object array of field values. If the list of {@code fieldNames} 86 | * is empty, you will get an empty array. 87 | */ 88 | public Object[] getFieldValues(String... fieldNames); 89 | 90 | /** 91 | * Removes a field from the descriptor. 92 | * 93 | * @param fieldName String name of the field to be removed. 94 | * If the field name is illegal or the field is not found, 95 | * no exception is thrown. 96 | * 97 | * @exception RuntimeOperationsException if a field of the given name 98 | * exists and the descriptor is immutable. The wrapped exception will 99 | * be an {@link UnsupportedOperationException}. 100 | */ 101 | public void removeField(String fieldName); 102 | 103 | /** 104 | *

Sets all fields in the field names array to the new value with 105 | * the same index in the field values array. Array sizes must match.

106 | * 107 | *

The field value will be validated before it is set. 108 | * If it is not valid, then an exception will be thrown. 109 | * If the arrays are empty, then no change will take effect.

110 | * 111 | * @param fieldNames String array of field names. The array and array 112 | * elements cannot be null. 113 | * @param fieldValues Object array of the corresponding field values. 114 | * The array cannot be null. Elements of the array can be null. 115 | * 116 | * @throws RuntimeOperationsException if the change fails for any reason. 117 | * Wrapped exception is {@link IllegalArgumentException} if 118 | * {@code fieldNames} or {@code fieldValues} is null, or if 119 | * the arrays are of different lengths, or if there is an 120 | * illegal value in one of them. 121 | * Wrapped exception is {@link UnsupportedOperationException} 122 | * if the descriptor is immutable, and the call would change 123 | * its contents. 124 | * 125 | * @see #getFields 126 | */ 127 | public void setFields(String[] fieldNames, Object[] fieldValues) 128 | throws RuntimeOperationsException; 129 | 130 | 131 | /** 132 | *

Returns a descriptor which is equal to this descriptor. 133 | * Changes to the returned descriptor will have no effect on this 134 | * descriptor, and vice versa. If this descriptor is immutable, 135 | * it may fulfill this condition by returning itself.

136 | * @exception RuntimeOperationsException for illegal value for field names 137 | * or field values. 138 | * If the descriptor construction fails for any reason, this exception will 139 | * be thrown. 140 | * @return A descriptor which is equal to this descriptor. 141 | */ 142 | public Object clone() throws RuntimeOperationsException; 143 | 144 | 145 | /** 146 | * Returns true if all of the fields have legal values given their 147 | * names. 148 | * 149 | * @return true if the values are legal. 150 | * 151 | * @exception RuntimeOperationsException If the validity checking fails for 152 | * any reason, this exception will be thrown. 153 | * The method returns false if the descriptor is not valid, but throws 154 | * this exception if the attempt to determine validity fails. 155 | */ 156 | public boolean isValid() throws RuntimeOperationsException; 157 | 158 | /** 159 | *

Compares this descriptor to the given object. The objects are equal if 160 | * the given object is also a Descriptor, and if the two Descriptors have 161 | * the same field names (possibly differing in case) and the same 162 | * associated values. The respective values for a field in the two 163 | * Descriptors are equal if the following conditions hold:

164 | * 165 | *
    166 | *
  • If one value is null then the other must be too.
  • 167 | *
  • If one value is a primitive array then the other must be a primitive 168 | * array of the same type with the same elements.
  • 169 | *
  • If one value is an object array then the other must be too and 170 | * {@link Arrays#deepEquals(Object[],Object[])} must return true.
  • 171 | *
  • Otherwise {@link Object#equals(Object)} must return true.
  • 172 | *
173 | * 174 | * @param obj the object to compare with. 175 | * 176 | * @return {@code true} if the objects are the same; {@code false} 177 | * otherwise. 178 | * 179 | * @since 1.6 180 | */ 181 | public boolean equals(Object obj); 182 | 183 | /** 184 | *

Returns the hash code value for this descriptor. The hash 185 | * code is computed as the sum of the hash codes for each field in 186 | * the descriptor. The hash code of a field with name {@code n} 187 | * and value {@code v} is {@code n.toLowerCase().hashCode() ^ h}. 188 | * Here {@code h} is the hash code of {@code v}, computed as 189 | * follows:

190 | * 191 | *
    192 | *
  • If {@code v} is null then {@code h} is 0.
  • 193 | *
  • If {@code v} is a primitive array then {@code h} is computed using 194 | * the appropriate overloading of {@code java.util.Arrays.hashCode}.
  • 195 | *
  • If {@code v} is an object array then {@code h} is computed using 196 | * {@link Arrays#deepHashCode(Object[])}.
  • 197 | *
  • Otherwise {@code h} is {@code v.hashCode()}.
  • 198 | *
199 | * 200 | * @return A hash code value for this object. 201 | * 202 | * @since 1.6 203 | */ 204 | public int hashCode(); 205 | } 206 | -------------------------------------------------------------------------------- /Implementor/basic/interfaces/standard/RandomAccess.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.basic.interfaces.standard; 2 | 3 | /** 4 | * Copy of {@link java.util.RandomAccess} 5 | */ 6 | public interface RandomAccess { 7 | } 8 | -------------------------------------------------------------------------------- /Implementor/basic/interfaces/standard/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Standard interfaces for basic tests 3 | * of Implementor homework 4 | * for Java Advanced course. 5 | * 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | package info.kgeorgiy.java.advanced.implementor.basic.interfaces.standard; -------------------------------------------------------------------------------- /Implementor/full/classes/ClassWithPackagePrivateConstructor.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.classes; 2 | 3 | /** 4 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 5 | */ 6 | public class ClassWithPackagePrivateConstructor { 7 | ClassWithPackagePrivateConstructor(final int ignore) { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Implementor/full/classes/Overridden.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.classes; 2 | 3 | /** 4 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 5 | */ 6 | public class Overridden { 7 | public static final Class[] OK = { 8 | Base.class, Child.class, 9 | FinalGrandChild.class, NonFinalGrandChild.class, 10 | SuperMethods.class, $.class, 11 | PrivateSuper.class 12 | }; 13 | 14 | public static final Class[] FAILED = {PrivateClass.class, PrivateArg.class, PrivateResult.class}; 15 | 16 | public static class Base { 17 | protected int protectedHello() { 18 | return 0; 19 | } 20 | public int publicHello() { 21 | return 0; 22 | } 23 | int packageHello() { 24 | return 0; 25 | } 26 | } 27 | 28 | @SuppressWarnings("AbstractMethodOverridesConcreteMethod") 29 | public static abstract class Child extends Base { 30 | @Override 31 | protected abstract int protectedHello(); 32 | 33 | @Override 34 | public abstract int publicHello(); 35 | 36 | @Override 37 | abstract int packageHello(); 38 | } 39 | 40 | public static abstract class FinalGrandChild extends Child { 41 | private final int value; 42 | 43 | public FinalGrandChild(int value) { 44 | this.value = value; 45 | } 46 | 47 | @Override 48 | protected final int protectedHello() { 49 | return value; 50 | } 51 | 52 | @Override 53 | public final int publicHello() { 54 | return value; 55 | } 56 | 57 | @Override 58 | final int packageHello() { 59 | return value; 60 | } 61 | } 62 | 63 | public static abstract class NonFinalGrandChild extends Child { 64 | } 65 | 66 | @SuppressWarnings("MethodNameSameAsClassName") 67 | public static abstract class SuperMethods extends Child { 68 | SuperMethods(final SuperMethods methods) { 69 | System.out.println(methods); 70 | } 71 | abstract void SuperMethods(SuperMethods methods); 72 | abstract void Child(Child child); 73 | abstract void Base(Base base); 74 | abstract void NonFinalGrandChild(NonFinalGrandChild child); 75 | } 76 | 77 | public static abstract class $ { 78 | abstract void $(); 79 | abstract void $$(); 80 | abstract void __(); 81 | } 82 | 83 | public abstract static class PrivateSuper extends PrivateClass { 84 | public PrivateSuper(final int value) { 85 | super(value); 86 | } 87 | } 88 | 89 | private static abstract class PrivateClass { 90 | private final int value; 91 | 92 | public PrivateClass(int value) { 93 | this.value = value; 94 | } 95 | 96 | abstract int hello(); 97 | } 98 | 99 | public static abstract class PrivateArg { 100 | abstract int get(PrivateClass arg); 101 | } 102 | 103 | public static abstract class PrivateResult { 104 | abstract PrivateClass get(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Implementor/full/classes/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Hand-made classes for tests 3 | * of Implementor homework 4 | * for Java Advanced course. 5 | * 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | package info.kgeorgiy.java.advanced.implementor.full.classes; -------------------------------------------------------------------------------- /Implementor/full/classes/standard/BMPImageWriteParam.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.classes.standard; 2 | 3 | import javax.imageio.ImageWriteParam; 4 | import java.util.Locale; 5 | 6 | /** 7 | * Copy of {@link javax.imageio.plugins.bmp.BMPImageWriteParam} 8 | */ 9 | public class BMPImageWriteParam extends ImageWriteParam { 10 | 11 | private boolean topDown = false; 12 | 13 | /** 14 | * Constructs a {@code BMPImageWriteParam} set to use a given 15 | * {@code Locale} and with default values for all parameters. 16 | * 17 | * @param locale a {@code Locale} to be used to localize 18 | * compression type names and quality descriptions, or 19 | * {@code null}. 20 | */ 21 | public BMPImageWriteParam(Locale locale) { 22 | super(locale); 23 | 24 | // Set compression types ("BI_RGB" denotes uncompressed). 25 | // Set compression flag. 26 | canWriteCompressed = true; 27 | compressionMode = MODE_COPY_FROM_METADATA; 28 | } 29 | 30 | /** 31 | * Constructs an {@code BMPImageWriteParam} object with default 32 | * values for all parameters and a {@code null Locale}. 33 | */ 34 | public BMPImageWriteParam() { 35 | this(null); 36 | } 37 | 38 | /** 39 | * If set, the data will be written out in a top-down manner, the first 40 | * scanline being written first. 41 | * 42 | * @param topDown whether the data are written in top-down order. 43 | */ 44 | public void setTopDown(boolean topDown) { 45 | this.topDown = topDown; 46 | } 47 | 48 | /** 49 | * Returns the value of the {@code topDown} parameter. 50 | * The default is {@code false}. 51 | * 52 | * @return whether the data are written in top-down order. 53 | */ 54 | public boolean isTopDown() { 55 | return topDown; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Implementor/full/classes/standard/FileCacheImageInputStream.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.classes.standard; 2 | 3 | import javax.imageio.stream.ImageInputStreamImpl; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.RandomAccessFile; 8 | import java.nio.file.Files; 9 | 10 | /** 11 | * Copy of {@link javax.imageio.stream.FileCacheImageInputStream} 12 | */ 13 | public class FileCacheImageInputStream extends ImageInputStreamImpl { 14 | 15 | private InputStream stream; 16 | 17 | private File cacheFile; 18 | 19 | private RandomAccessFile cache; 20 | 21 | private static final int BUFFER_LENGTH = 1024; 22 | 23 | private byte[] buf = new byte[BUFFER_LENGTH]; 24 | 25 | private long length = 0L; 26 | 27 | private boolean foundEOF = false; 28 | 29 | /** 30 | * Constructs a {@code FileCacheImageInputStream} that will read 31 | * from a given {@code InputStream}. 32 | * 33 | *

A temporary file is used as a cache. If 34 | * {@code cacheDir} is non-{@code null} and is a 35 | * directory, the file will be created there. If it is 36 | * {@code null}, the system-dependent default temporary-file 37 | * directory will be used (see the documentation for 38 | * {@code File.createTempFile} for details). 39 | * 40 | * @param stream an {@code InputStream} to read from. 41 | * @param cacheDir a {@code File} indicating where the 42 | * cache file should be created, or {@code null} to use the 43 | * system directory. 44 | * 45 | * @exception IllegalArgumentException if {@code stream} is 46 | * {@code null}. 47 | * @exception IllegalArgumentException if {@code cacheDir} is 48 | * non-{@code null} but is not a directory. 49 | * @throws IOException if a cache file cannot be created. 50 | */ 51 | public FileCacheImageInputStream(InputStream stream, File cacheDir) 52 | throws IOException { 53 | if (stream == null) { 54 | throw new IllegalArgumentException("stream == null!"); 55 | } 56 | if ((cacheDir != null) && !(cacheDir.isDirectory())) { 57 | throw new IllegalArgumentException("Not a directory!"); 58 | } 59 | this.stream = stream; 60 | if (cacheDir == null) 61 | this.cacheFile = Files.createTempFile("imageio", ".tmp").toFile(); 62 | else 63 | this.cacheFile = Files.createTempFile(cacheDir.toPath(), "imageio", ".tmp") 64 | .toFile(); 65 | this.cache = new RandomAccessFile(cacheFile, "rw"); 66 | } 67 | 68 | /** 69 | * Ensures that at least {@code pos} bytes are cached, 70 | * or the end of the source is reached. The return value 71 | * is equal to the smaller of {@code pos} and the 72 | * length of the source file. 73 | * 74 | * @throws IOException if an I/O error occurs while reading from the 75 | * source file 76 | */ 77 | private long readUntil(long pos) throws IOException { 78 | // We've already got enough data cached 79 | if (pos < length) { 80 | return pos; 81 | } 82 | // pos >= length but length isn't getting any bigger, so return it 83 | if (foundEOF) { 84 | return length; 85 | } 86 | 87 | long len = pos - length; 88 | cache.seek(length); 89 | while (len > 0) { 90 | // Copy a buffer's worth of data from the source to the cache 91 | // BUFFER_LENGTH will always fit into an int so this is safe 92 | int nbytes = 93 | stream.read(buf, 0, (int)Math.min(len, (long)BUFFER_LENGTH)); 94 | if (nbytes == -1) { 95 | foundEOF = true; 96 | return length; 97 | } 98 | 99 | cache.write(buf, 0, nbytes); 100 | len -= nbytes; 101 | length += nbytes; 102 | } 103 | 104 | return pos; 105 | } 106 | 107 | public int read() throws IOException { 108 | checkClosed(); 109 | bitOffset = 0; 110 | long next = streamPos + 1; 111 | long pos = readUntil(next); 112 | if (pos >= next) { 113 | cache.seek(streamPos++); 114 | return cache.read(); 115 | } else { 116 | return -1; 117 | } 118 | } 119 | 120 | public int read(byte[] b, int off, int len) throws IOException { 121 | checkClosed(); 122 | 123 | if (b == null) { 124 | throw new NullPointerException("b == null!"); 125 | } 126 | // Fix 4430357 - if off + len < 0, overflow occurred 127 | if (off < 0 || len < 0 || off + len > b.length || off + len < 0) { 128 | throw new IndexOutOfBoundsException 129 | ("off < 0 || len < 0 || off+len > b.length || off+len < 0!"); 130 | } 131 | 132 | bitOffset = 0; 133 | 134 | if (len == 0) { 135 | return 0; 136 | } 137 | 138 | long pos = readUntil(streamPos + len); 139 | 140 | // len will always fit into an int so this is safe 141 | len = (int)Math.min((long)len, pos - streamPos); 142 | if (len > 0) { 143 | cache.seek(streamPos); 144 | cache.readFully(b, off, len); 145 | streamPos += len; 146 | return len; 147 | } else { 148 | return -1; 149 | } 150 | } 151 | 152 | /** 153 | * Returns {@code true} since this 154 | * {@code ImageInputStream} caches data in order to allow 155 | * seeking backwards. 156 | * 157 | * @return {@code true}. 158 | * 159 | * @see #isCachedMemory 160 | * @see #isCachedFile 161 | */ 162 | public boolean isCached() { 163 | return true; 164 | } 165 | 166 | /** 167 | * Returns {@code true} since this 168 | * {@code ImageInputStream} maintains a file cache. 169 | * 170 | * @return {@code true}. 171 | * 172 | * @see #isCached 173 | * @see #isCachedMemory 174 | */ 175 | public boolean isCachedFile() { 176 | return true; 177 | } 178 | 179 | /** 180 | * Returns {@code false} since this 181 | * {@code ImageInputStream} does not maintain a main memory 182 | * cache. 183 | * 184 | * @return {@code false}. 185 | * 186 | * @see #isCached 187 | * @see #isCachedFile 188 | */ 189 | public boolean isCachedMemory() { 190 | return false; 191 | } 192 | 193 | /** 194 | * Closes this {@code FileCacheImageInputStream}, closing 195 | * and removing the cache file. The source {@code InputStream} 196 | * is not closed. 197 | * 198 | * @throws IOException if an error occurs. 199 | */ 200 | public void close() throws IOException { 201 | super.close(); 202 | stream = null; 203 | cache = null; 204 | cacheFile = null; 205 | } 206 | 207 | } 208 | -------------------------------------------------------------------------------- /Implementor/full/classes/standard/ImmutableDescriptor.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.classes.standard; 2 | 3 | import javax.management.Descriptor; 4 | import javax.management.RuntimeOperationsException; 5 | import java.io.InvalidObjectException; 6 | import java.lang.reflect.Array; 7 | import java.util.*; 8 | 9 | /** 10 | * Copy of {@link ImmutableDescriptor} 11 | */ 12 | public class ImmutableDescriptor implements Descriptor { 13 | private static final long serialVersionUID = 8853308591080540165L; 14 | 15 | /** 16 | * The names of the fields in this ImmutableDescriptor with their 17 | * original case. The names must be in alphabetical order as determined 18 | * by {@link String#CASE_INSENSITIVE_ORDER}. 19 | */ 20 | private final String[] names; 21 | /** 22 | * The values of the fields in this ImmutableDescriptor. The 23 | * elements in this array match the corresponding elements in the 24 | * {@code names} array. 25 | */ 26 | private final Object[] values; 27 | 28 | private transient int hashCode = -1; 29 | 30 | /** 31 | * An empty descriptor. 32 | */ 33 | public static final ImmutableDescriptor EMPTY_DESCRIPTOR = 34 | new ImmutableDescriptor(); 35 | 36 | /** 37 | * Construct a descriptor containing the given fields and values. 38 | * 39 | * @param fieldNames the field names 40 | * @param fieldValues the field values 41 | * @throws IllegalArgumentException if either array is null, or 42 | * if the arrays have different sizes, or 43 | * if a field name is null or empty, or if the same field name 44 | * appears more than once. 45 | */ 46 | public ImmutableDescriptor(String[] fieldNames, Object[] fieldValues) { 47 | this(makeMap(fieldNames, fieldValues)); 48 | } 49 | 50 | /** 51 | * Construct a descriptor containing the given fields. Each String 52 | * must be of the form {@code fieldName=fieldValue}. The field name 53 | * ends at the first {@code =} character; for example if the String 54 | * is {@code a=b=c} then the field name is {@code a} and its value 55 | * is {@code b=c}. 56 | * 57 | * @param fields the field names 58 | * @throws IllegalArgumentException if the parameter is null, or 59 | * if a field name is empty, or if the same field name appears 60 | * more than once, or if one of the strings does not contain 61 | * an {@code =} character. 62 | */ 63 | public ImmutableDescriptor(String... fields) { 64 | this(makeMap(fields)); 65 | } 66 | 67 | /** 68 | *

Construct a descriptor where the names and values of the fields 69 | * are the keys and values of the given Map.

70 | * 71 | * @param fields the field names and values 72 | * @throws IllegalArgumentException if the parameter is null, or 73 | * if a field name is null or empty, or if the same field name appears 74 | * more than once (which can happen because field names are not case 75 | * sensitive). 76 | */ 77 | public ImmutableDescriptor(Map fields) { 78 | if (fields == null) 79 | throw new IllegalArgumentException("Null Map"); 80 | SortedMap map = 81 | new TreeMap(String.CASE_INSENSITIVE_ORDER); 82 | for (Map.Entry entry : fields.entrySet()) { 83 | String name = entry.getKey(); 84 | if (name == null || name.equals("")) 85 | throw new IllegalArgumentException("Empty or null field name"); 86 | if (map.containsKey(name)) 87 | throw new IllegalArgumentException("Duplicate name: " + name); 88 | map.put(name, entry.getValue()); 89 | } 90 | int size = map.size(); 91 | this.names = map.keySet().toArray(new String[size]); 92 | this.values = map.values().toArray(new Object[size]); 93 | } 94 | 95 | /** 96 | * This method can replace a deserialized instance of this 97 | * class with another instance. For example, it might replace 98 | * a deserialized empty ImmutableDescriptor with 99 | * {@link #EMPTY_DESCRIPTOR}. 100 | * 101 | * @return the replacement object, which may be {@code this}. 102 | * 103 | * @throws InvalidObjectException if the read object has invalid fields. 104 | */ 105 | private Object readResolve() throws InvalidObjectException { 106 | 107 | boolean bad = false; 108 | if (names == null || values == null || names.length != values.length) 109 | bad = true; 110 | if (!bad) { 111 | if (names.length == 0 && getClass() == ImmutableDescriptor.class) 112 | return EMPTY_DESCRIPTOR; 113 | final Comparator compare = String.CASE_INSENSITIVE_ORDER; 114 | String lastName = ""; // also catches illegal null name 115 | for (int i = 0; i < names.length; i++) { 116 | if (names[i] == null || 117 | compare.compare(lastName, names[i]) >= 0) { 118 | bad = true; 119 | break; 120 | } 121 | lastName = names[i]; 122 | } 123 | } 124 | if (bad) 125 | throw new InvalidObjectException("Bad names or values"); 126 | 127 | return this; 128 | } 129 | 130 | private static SortedMap makeMap(String[] fieldNames, 131 | Object[] fieldValues) { 132 | if (fieldNames == null || fieldValues == null) 133 | throw new IllegalArgumentException("Null array parameter"); 134 | if (fieldNames.length != fieldValues.length) 135 | throw new IllegalArgumentException("Different size arrays"); 136 | SortedMap map = 137 | new TreeMap(String.CASE_INSENSITIVE_ORDER); 138 | for (int i = 0; i < fieldNames.length; i++) { 139 | String name = fieldNames[i]; 140 | if (name == null || name.equals("")) 141 | throw new IllegalArgumentException("Empty or null field name"); 142 | Object old = map.put(name, fieldValues[i]); 143 | if (old != null) { 144 | throw new IllegalArgumentException("Duplicate field name: " + 145 | name); 146 | } 147 | } 148 | return map; 149 | } 150 | 151 | private static SortedMap makeMap(String[] fields) { 152 | if (fields == null) 153 | throw new IllegalArgumentException("Null fields parameter"); 154 | String[] fieldNames = new String[fields.length]; 155 | String[] fieldValues = new String[fields.length]; 156 | for (int i = 0; i < fields.length; i++) { 157 | String field = fields[i]; 158 | int eq = field.indexOf('='); 159 | if (eq < 0) { 160 | throw new IllegalArgumentException("Missing = character: " + 161 | field); 162 | } 163 | fieldNames[i] = field.substring(0, eq); 164 | // makeMap will catch the case where the name is empty 165 | fieldValues[i] = field.substring(eq + 1); 166 | } 167 | return makeMap(fieldNames, fieldValues); 168 | } 169 | 170 | /** 171 | *

Return an {@code ImmutableDescriptor} whose contents are the union of 172 | * the given descriptors. Every field name that appears in any of 173 | * the descriptors will appear in the result with the 174 | * value that it has when the method is called. Subsequent changes 175 | * to any of the descriptors do not affect the ImmutableDescriptor 176 | * returned here.

177 | * 178 | *

In the simplest case, there is only one descriptor and the 179 | * returned {@code ImmutableDescriptor} is a copy of its fields at the 180 | * time this method is called:

181 | * 182 | *
183 |      * Descriptor d = something();
184 |      * ImmutableDescriptor copy = ImmutableDescriptor.union(d);
185 |      * 
186 | * 187 | * @param descriptors the descriptors to be combined. Any of the 188 | * descriptors can be null, in which case it is skipped. 189 | * 190 | * @return an {@code ImmutableDescriptor} that is the union of the given 191 | * descriptors. The returned object may be identical to one of the 192 | * input descriptors if it is an ImmutableDescriptor that contains all of 193 | * the required fields. 194 | * 195 | * @throws IllegalArgumentException if two Descriptors contain the 196 | * same field name with different associated values. Primitive array 197 | * values are considered the same if they are of the same type with 198 | * the same elements. Object array values are considered the same if 199 | * {@link Arrays#deepEquals(Object[],Object[])} returns true. 200 | */ 201 | public static ImmutableDescriptor union(Descriptor... descriptors) { 202 | // Optimize the case where exactly one Descriptor is non-Empty 203 | // and it is immutable - we can just return it. 204 | int index = findNonEmpty(descriptors, 0); 205 | if (index < 0) 206 | return EMPTY_DESCRIPTOR; 207 | if (descriptors[index] instanceof ImmutableDescriptor 208 | && findNonEmpty(descriptors, index + 1) < 0) 209 | return (ImmutableDescriptor) descriptors[index]; 210 | 211 | Map map = 212 | new TreeMap(String.CASE_INSENSITIVE_ORDER); 213 | ImmutableDescriptor biggestImmutable = EMPTY_DESCRIPTOR; 214 | for (Descriptor d : descriptors) { 215 | if (d != null) { 216 | String[] names; 217 | if (d instanceof ImmutableDescriptor) { 218 | ImmutableDescriptor id = (ImmutableDescriptor) d; 219 | names = id.names; 220 | if (id.getClass() == ImmutableDescriptor.class 221 | && names.length > biggestImmutable.names.length) 222 | biggestImmutable = id; 223 | } else 224 | names = d.getFieldNames(); 225 | for (String n : names) { 226 | Object v = d.getFieldValue(n); 227 | Object old = map.put(n, v); 228 | if (old != null) { 229 | boolean equal; 230 | if (old.getClass().isArray()) { 231 | equal = Arrays.deepEquals(new Object[] {old}, 232 | new Object[] {v}); 233 | } else 234 | equal = old.equals(v); 235 | if (!equal) { 236 | final String msg = 237 | "Inconsistent values for descriptor field " + 238 | n + ": " + old + " :: " + v; 239 | throw new IllegalArgumentException(msg); 240 | } 241 | } 242 | } 243 | } 244 | } 245 | if (biggestImmutable.names.length == map.size()) 246 | return biggestImmutable; 247 | return new ImmutableDescriptor(map); 248 | } 249 | 250 | private static boolean isEmpty(Descriptor d) { 251 | if (d == null) 252 | return true; 253 | else if (d instanceof ImmutableDescriptor) 254 | return ((ImmutableDescriptor) d).names.length == 0; 255 | else 256 | return (d.getFieldNames().length == 0); 257 | } 258 | 259 | private static int findNonEmpty(Descriptor[] ds, int start) { 260 | for (int i = start; i < ds.length; i++) { 261 | if (!isEmpty(ds[i])) 262 | return i; 263 | } 264 | return -1; 265 | } 266 | 267 | private int fieldIndex(String name) { 268 | return Arrays.binarySearch(names, name, String.CASE_INSENSITIVE_ORDER); 269 | } 270 | 271 | public final Object getFieldValue(String fieldName) { 272 | checkIllegalFieldName(fieldName); 273 | int i = fieldIndex(fieldName); 274 | if (i < 0) 275 | return null; 276 | Object v = values[i]; 277 | if (v == null || !v.getClass().isArray()) 278 | return v; 279 | if (v instanceof Object[]) 280 | return ((Object[]) v).clone(); 281 | // clone the primitive array, could use an 8-way if/else here 282 | int len = Array.getLength(v); 283 | Object a = Array.newInstance(v.getClass().getComponentType(), len); 284 | System.arraycopy(v, 0, a, 0, len); 285 | return a; 286 | } 287 | 288 | public final String[] getFields() { 289 | String[] result = new String[names.length]; 290 | for (int i = 0; i < result.length; i++) { 291 | Object value = values[i]; 292 | if (value == null) 293 | value = ""; 294 | else if (!(value instanceof String)) 295 | value = "(" + value + ")"; 296 | result[i] = names[i] + "=" + value; 297 | } 298 | return result; 299 | } 300 | 301 | public final Object[] getFieldValues(String... fieldNames) { 302 | if (fieldNames == null) 303 | return values.clone(); 304 | Object[] result = new Object[fieldNames.length]; 305 | for (int i = 0; i < fieldNames.length; i++) { 306 | String name = fieldNames[i]; 307 | if (name != null && !name.equals("")) 308 | result[i] = getFieldValue(name); 309 | } 310 | return result; 311 | } 312 | 313 | public final String[] getFieldNames() { 314 | return names.clone(); 315 | } 316 | 317 | /** 318 | * Compares this descriptor to the given object. The objects are equal if 319 | * the given object is also a Descriptor, and if the two Descriptors have 320 | * the same field names (possibly differing in case) and the same 321 | * associated values. The respective values for a field in the two 322 | * Descriptors are equal if the following conditions hold: 323 | * 324 | *
    325 | *
  • If one value is null then the other must be too.
  • 326 | *
  • If one value is a primitive array then the other must be a primitive 327 | * array of the same type with the same elements.
  • 328 | *
  • If one value is an object array then the other must be too and 329 | * {@link Arrays#deepEquals(Object[],Object[])} must return true.
  • 330 | *
  • Otherwise {@link Object#equals(Object)} must return true.
  • 331 | *
332 | * 333 | * @param o the object to compare with. 334 | * 335 | * @return {@code true} if the objects are the same; {@code false} 336 | * otherwise. 337 | * 338 | */ 339 | // Note: this Javadoc is copied from javax.management.Descriptor 340 | // due to 6369229. 341 | @Override 342 | public boolean equals(Object o) { 343 | if (o == this) 344 | return true; 345 | if (!(o instanceof Descriptor)) 346 | return false; 347 | String[] onames; 348 | if (o instanceof ImmutableDescriptor) { 349 | onames = ((ImmutableDescriptor) o).names; 350 | } else { 351 | onames = ((Descriptor) o).getFieldNames(); 352 | Arrays.sort(onames, String.CASE_INSENSITIVE_ORDER); 353 | } 354 | if (names.length != onames.length) 355 | return false; 356 | for (int i = 0; i < names.length; i++) { 357 | if (!names[i].equalsIgnoreCase(onames[i])) 358 | return false; 359 | } 360 | Object[] ovalues; 361 | if (o instanceof ImmutableDescriptor) 362 | ovalues = ((ImmutableDescriptor) o).values; 363 | else 364 | ovalues = ((Descriptor) o).getFieldValues(onames); 365 | return Arrays.deepEquals(values, ovalues); 366 | } 367 | 368 | /** 369 | *

Returns the hash code value for this descriptor. The hash 370 | * code is computed as the sum of the hash codes for each field in 371 | * the descriptor. The hash code of a field with name {@code n} 372 | * and value {@code v} is {@code n.toLowerCase().hashCode() ^ h}. 373 | * Here {@code h} is the hash code of {@code v}, computed as 374 | * follows:

375 | * 376 | *
    377 | *
  • If {@code v} is null then {@code h} is 0.
  • 378 | *
  • If {@code v} is a primitive array then {@code h} is computed using 379 | * the appropriate overloading of {@code java.util.Arrays.hashCode}.
  • 380 | *
  • If {@code v} is an object array then {@code h} is computed using 381 | * {@link Arrays#deepHashCode(Object[])}.
  • 382 | *
  • Otherwise {@code h} is {@code v.hashCode()}.
  • 383 | *
384 | * 385 | * @return A hash code value for this object. 386 | * 387 | */ 388 | // Note: this Javadoc is copied from javax.management.Descriptor 389 | // due to 6369229. 390 | @Override 391 | public int hashCode() { 392 | if (hashCode == -1) { 393 | hashCode = Objects.hash(names, values); 394 | } 395 | return hashCode; 396 | } 397 | 398 | @Override 399 | public String toString() { 400 | StringBuilder sb = new StringBuilder("{"); 401 | for (int i = 0; i < names.length; i++) { 402 | if (i > 0) 403 | sb.append(", "); 404 | sb.append(names[i]).append("="); 405 | Object v = values[i]; 406 | if (v != null && v.getClass().isArray()) { 407 | String s = Arrays.deepToString(new Object[] {v}); 408 | s = s.substring(1, s.length() - 1); // remove [...] 409 | v = s; 410 | } 411 | sb.append(String.valueOf(v)); 412 | } 413 | return sb.append("}").toString(); 414 | } 415 | 416 | /** 417 | * Returns true if all of the fields have legal values given their 418 | * names. This method always returns true, but a subclass can 419 | * override it to return false when appropriate. 420 | * 421 | * @return true if the values are legal. 422 | * 423 | * @exception RuntimeOperationsException if the validity checking fails. 424 | * The method returns false if the descriptor is not valid, but throws 425 | * this exception if the attempt to determine validity fails. 426 | */ 427 | public boolean isValid() { 428 | return true; 429 | } 430 | 431 | /** 432 | *

Returns a descriptor which is equal to this descriptor. 433 | * Changes to the returned descriptor will have no effect on this 434 | * descriptor, and vice versa.

435 | * 436 | *

This method returns the object on which it is called. 437 | * A subclass can override it 438 | * to return another object provided the contract is respected. 439 | * 440 | * @exception RuntimeOperationsException for illegal value for field Names 441 | * or field Values. 442 | * If the descriptor construction fails for any reason, this exception will 443 | * be thrown. 444 | */ 445 | @Override 446 | public Descriptor clone() { 447 | return this; 448 | } 449 | 450 | /** 451 | * This operation is unsupported since this class is immutable. If 452 | * this call would change a mutable descriptor with the same contents, 453 | * then a {@link RuntimeOperationsException} wrapping an 454 | * {@link UnsupportedOperationException} is thrown. Otherwise, 455 | * the behavior is the same as it would be for a mutable descriptor: 456 | * either an exception is thrown because of illegal parameters, or 457 | * there is no effect. 458 | */ 459 | public final void setFields(String[] fieldNames, Object[] fieldValues) 460 | throws RuntimeOperationsException { 461 | if (fieldNames == null || fieldValues == null) 462 | illegal("Null argument"); 463 | if (fieldNames.length != fieldValues.length) 464 | illegal("Different array sizes"); 465 | for (int i = 0; i < fieldNames.length; i++) 466 | checkIllegalFieldName(fieldNames[i]); 467 | for (int i = 0; i < fieldNames.length; i++) 468 | setField(fieldNames[i], fieldValues[i]); 469 | } 470 | 471 | /** 472 | * This operation is unsupported since this class is immutable. If 473 | * this call would change a mutable descriptor with the same contents, 474 | * then a {@link RuntimeOperationsException} wrapping an 475 | * {@link UnsupportedOperationException} is thrown. Otherwise, 476 | * the behavior is the same as it would be for a mutable descriptor: 477 | * either an exception is thrown because of illegal parameters, or 478 | * there is no effect. 479 | */ 480 | public final void setField(String fieldName, Object fieldValue) 481 | throws RuntimeOperationsException { 482 | checkIllegalFieldName(fieldName); 483 | int i = fieldIndex(fieldName); 484 | if (i < 0) 485 | unsupported(); 486 | Object value = values[i]; 487 | if ((value == null) ? 488 | (fieldValue != null) : 489 | !value.equals(fieldValue)) 490 | unsupported(); 491 | } 492 | 493 | /** 494 | * Removes a field from the descriptor. 495 | * 496 | * @param fieldName String name of the field to be removed. 497 | * If the field name is illegal or the field is not found, 498 | * no exception is thrown. 499 | * 500 | * @exception RuntimeOperationsException if a field of the given name 501 | * exists and the descriptor is immutable. The wrapped exception will 502 | * be an {@link UnsupportedOperationException}. 503 | */ 504 | public final void removeField(String fieldName) { 505 | if (fieldName != null && fieldIndex(fieldName) >= 0) 506 | unsupported(); 507 | } 508 | 509 | static Descriptor nonNullDescriptor(Descriptor d) { 510 | if (d == null) 511 | return EMPTY_DESCRIPTOR; 512 | else 513 | return d; 514 | } 515 | 516 | private static void checkIllegalFieldName(String name) { 517 | if (name == null || name.equals("")) 518 | illegal("Null or empty field name"); 519 | } 520 | 521 | private static void unsupported() { 522 | UnsupportedOperationException uoe = 523 | new UnsupportedOperationException("Descriptor is read-only"); 524 | throw new RuntimeOperationsException(uoe); 525 | } 526 | 527 | private static void illegal(String message) { 528 | IllegalArgumentException iae = new IllegalArgumentException(message); 529 | throw new RuntimeOperationsException(iae); 530 | } 531 | } 532 | -------------------------------------------------------------------------------- /Implementor/full/classes/standard/LdapReferralException.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.classes.standard; 2 | 3 | import javax.naming.Context; 4 | import javax.naming.NamingException; 5 | import javax.naming.ReferralException; 6 | import javax.naming.ldap.Control; 7 | import java.util.Hashtable; 8 | 9 | /** 10 | * Copy of {@link javax.naming.ldap.LdapReferralException} 11 | */ 12 | public abstract class LdapReferralException extends ReferralException { 13 | /** 14 | * Constructs a new instance of LdapReferralException using the 15 | * explanation supplied. All other fields are set to null. 16 | * 17 | * @param explanation Additional detail about this exception. Can be null. 18 | * @see Throwable#getMessage 19 | */ 20 | protected LdapReferralException(String explanation) { 21 | super(explanation); 22 | } 23 | 24 | /** 25 | * Constructs a new instance of LdapReferralException. 26 | * All fields are set to null. 27 | */ 28 | protected LdapReferralException() { 29 | super(); 30 | } 31 | 32 | /** 33 | * Retrieves the context at which to continue the method using the 34 | * context's environment and no controls. 35 | * The referral context is created using the environment properties of 36 | * the context that threw the {@code ReferralException} and no controls. 37 | *

38 | * This method is equivalent to 39 | *

 40 |      * getReferralContext(ctx.getEnvironment(), null);
 41 |      *
42 | * where {@code ctx} is the context that threw the {@code ReferralException.} 43 | *

44 | * It is overridden in this class for documentation purposes only. 45 | * See {@code ReferralException} for how to use this method. 46 | * 47 | * @return The non-null context at which to continue the method. 48 | * @exception NamingException If a naming exception was encountered. 49 | * Call either {@code retryReferral()} or {@code skipReferral()} 50 | * to continue processing referrals. 51 | */ 52 | public abstract Context getReferralContext() throws NamingException; 53 | 54 | /** 55 | * Retrieves the context at which to continue the method using 56 | * environment properties and no controls. 57 | * The referral context is created using {@code env} as its environment 58 | * properties and no controls. 59 | *

60 | * This method is equivalent to 61 | *

 62 |      * getReferralContext(env, null);
 63 |      *
64 | *

65 | * It is overridden in this class for documentation purposes only. 66 | * See {@code ReferralException} for how to use this method. 67 | * 68 | * @param env The possibly null environment to use when retrieving the 69 | * referral context. If null, no environment properties will be used. 70 | * 71 | * @return The non-null context at which to continue the method. 72 | * @exception NamingException If a naming exception was encountered. 73 | * Call either {@code retryReferral()} or {@code skipReferral()} 74 | * to continue processing referrals. 75 | */ 76 | public abstract Context 77 | getReferralContext(Hashtable env) 78 | throws NamingException; 79 | 80 | /** 81 | * Retrieves the context at which to continue the method using 82 | * request controls and environment properties. 83 | * Regardless of whether a referral is encountered directly during a 84 | * context operation, or indirectly, for example, during a search 85 | * enumeration, the referral exception should provide a context 86 | * at which to continue the operation. 87 | * To continue the operation, the client program should re-invoke 88 | * the method using the same arguments as the original invocation. 89 | *

90 | * {@code reqCtls} is used when creating the connection to the referred 91 | * server. These controls will be used as the connection request controls for 92 | * the context and context instances 93 | * derived from the context. 94 | * {@code reqCtls} will also be the context's request controls for 95 | * subsequent context operations. See the {@code LdapContext} class 96 | * description for details. 97 | *

98 | * This method should be used instead of the other two overloaded forms 99 | * when the caller needs to supply request controls for creating 100 | * the referral context. It might need to do this, for example, when 101 | * it needs to supply special controls relating to authentication. 102 | *

103 | * Service provider implementors should read the "Service Provider" section 104 | * in the {@code LdapContext} class description for implementation details. 105 | * 106 | * @param reqCtls The possibly null request controls to use for the new context. 107 | * If null or the empty array means use no request controls. 108 | * @param env The possibly null environment properties to use when 109 | * for the new context. If null, the context is initialized with no environment 110 | * properties. 111 | * @return The non-null context at which to continue the method. 112 | * @exception NamingException If a naming exception was encountered. 113 | * Call either {@code retryReferral()} or {@code skipReferral()} 114 | * to continue processing referrals. 115 | */ 116 | public abstract Context 117 | getReferralContext(Hashtable env, 118 | Control[] reqCtls) 119 | throws NamingException; 120 | 121 | private static final long serialVersionUID = -1668992791764950804L; 122 | } 123 | -------------------------------------------------------------------------------- /Implementor/full/classes/standard/RMIIIOPServerImpl.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.classes.standard; 2 | 3 | import javax.management.remote.rmi.RMIConnection; 4 | import javax.management.remote.rmi.RMIServerImpl; 5 | import javax.security.auth.Subject; 6 | import java.io.IOException; 7 | import java.rmi.Remote; 8 | import java.util.Map; 9 | 10 | /** 11 | * Copy of {@link javax.management.remote.rmi.RMIIIOPServerImpl} 12 | */ 13 | public class RMIIIOPServerImpl extends RMIServerImpl { 14 | /** 15 | * Throws {@linkplain UnsupportedOperationException} 16 | * 17 | * @param env the environment containing attributes for the new 18 | * RMIServerImpl. Can be null, which is equivalent 19 | * to an empty Map. 20 | * 21 | * @throws IOException if the RMI object cannot be created. 22 | */ 23 | public RMIIIOPServerImpl(Map env) 24 | throws IOException { 25 | super(env); 26 | 27 | throw new UnsupportedOperationException(); 28 | } 29 | 30 | @Override 31 | protected void export() throws IOException { 32 | throw new UnsupportedOperationException("Method not supported. JMX RMI-IIOP is deprecated"); 33 | } 34 | 35 | @Override 36 | protected String getProtocol() { 37 | return "iiop"; 38 | } 39 | 40 | @Override 41 | public Remote toStub() throws IOException { 42 | throw new UnsupportedOperationException(); 43 | } 44 | 45 | @Override 46 | protected RMIConnection makeClient(String connectionId, Subject subject) 47 | throws IOException { 48 | throw new UnsupportedOperationException(); 49 | } 50 | 51 | @Override 52 | protected void closeClient(RMIConnection client) throws IOException { 53 | throw new UnsupportedOperationException(); 54 | } 55 | 56 | @Override 57 | protected void closeServer() throws IOException { 58 | throw new UnsupportedOperationException(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Implementor/full/classes/standard/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Standard classes for tests 3 | * of Implementor homework 4 | * for Java Advanced course. 5 | * 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | package info.kgeorgiy.java.advanced.implementor.full.classes.standard; -------------------------------------------------------------------------------- /Implementor/full/interfaces/InterfaceWithoutMethods.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.interfaces; 2 | 3 | import java.util.RandomAccess; 4 | 5 | /** 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | public interface InterfaceWithoutMethods extends RandomAccess { 9 | } 10 | -------------------------------------------------------------------------------- /Implementor/full/interfaces/Interfaces.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.interfaces; 2 | 3 | /** 4 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 5 | */ 6 | public class Interfaces { 7 | public static final Class[] OK = { 8 | PublicInterface.class, 9 | PackagePrivateInterface.class, 10 | InheritedInterface.class, 11 | ProtectedInterface.class 12 | }; 13 | public static final Class[] FAILED = { 14 | PrivateInterface.class 15 | }; 16 | 17 | public interface PublicInterface { 18 | String hello(); 19 | } 20 | 21 | protected interface ProtectedInterface { 22 | String hello(); 23 | } 24 | 25 | interface PackagePrivateInterface { 26 | String hello(); 27 | } 28 | 29 | private interface PrivateInterface { 30 | String hello(); 31 | } 32 | 33 | interface InheritedInterface extends PrivateInterface { 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Implementor/full/interfaces/Proxies.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.interfaces; 2 | 3 | /** 4 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 5 | */ 6 | public interface Proxies { 7 | java.lang.reflect.Proxy getReflectionProxy(); 8 | java.net.Proxy getNetworkProxy(); 9 | } 10 | -------------------------------------------------------------------------------- /Implementor/full/interfaces/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Hand-made interfaces for tests 3 | * of Implementor homework 4 | * for Java Advanced course. 5 | * 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | package info.kgeorgiy.java.advanced.implementor.full.interfaces; -------------------------------------------------------------------------------- /Implementor/full/interfaces/standard/AccessibleAction.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.interfaces.standard; 2 | 3 | /** 4 | * Copy of {@link javax.accessibility.AccessibleAction} 5 | */ 6 | public interface AccessibleAction { 7 | 8 | /** 9 | * An action which causes a tree node to collapse if expanded and expand if 10 | * collapsed. 11 | * 12 | * @since 1.5 13 | */ 14 | public static final String TOGGLE_EXPAND = 15 | new String ("toggleexpand"); 16 | 17 | /** 18 | * An action which increments a value. 19 | * 20 | * @since 1.5 21 | */ 22 | public static final String INCREMENT = 23 | new String ("increment"); 24 | 25 | 26 | /** 27 | * An action which decrements a value. 28 | * 29 | * @since 1.5 30 | */ 31 | public static final String DECREMENT = 32 | new String ("decrement"); 33 | 34 | /** 35 | * An action which causes a component to execute its default action. 36 | * 37 | * @since 1.6 38 | */ 39 | public static final String CLICK = new String("click"); 40 | 41 | /** 42 | * An action which causes a popup to become visible if it is hidden and 43 | * hidden if it is visible. 44 | * 45 | * @since 1.6 46 | */ 47 | public static final String TOGGLE_POPUP = new String("toggle popup"); 48 | 49 | /** 50 | * Returns the number of accessible actions available in this object If 51 | * there are more than one, the first one is considered the "default" action 52 | * of the object. 53 | * 54 | * @return the zero-based number of actions in this object 55 | */ 56 | public int getAccessibleActionCount(); 57 | 58 | /** 59 | * Returns a description of the specified action of the object. 60 | * 61 | * @param i zero-based index of the actions 62 | * @return a {@code String} description of the action 63 | * @see #getAccessibleActionCount 64 | */ 65 | public String getAccessibleActionDescription(int i); 66 | 67 | /** 68 | * Performs the specified action on the object. 69 | * 70 | * @param i zero-based index of actions 71 | * @return {@code true} if the action was performed; otherwise {@code false} 72 | * @see #getAccessibleActionCount 73 | */ 74 | public boolean doAccessibleAction(int i); 75 | } 76 | -------------------------------------------------------------------------------- /Implementor/full/interfaces/standard/DataInput.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.interfaces.standard; 2 | 3 | import java.io.EOFException; 4 | import java.io.IOException; 5 | 6 | /** 7 | * Copy of {@link java.io.DataInput} 8 | */ 9 | public 10 | interface DataInput { 11 | /** 12 | * Reads some bytes from an input 13 | * stream and stores them into the buffer 14 | * array {@code b}. The number of bytes 15 | * read is equal 16 | * to the length of {@code b}. 17 | *

18 | * This method blocks until one of the 19 | * following conditions occurs: 20 | *

    21 | *
  • {@code b.length} 22 | * bytes of input data are available, in which 23 | * case a normal return is made. 24 | * 25 | *
  • End of 26 | * file is detected, in which case an {@code EOFException} 27 | * is thrown. 28 | * 29 | *
  • An I/O error occurs, in 30 | * which case an {@code IOException} other 31 | * than {@code EOFException} is thrown. 32 | *
33 | *

34 | * If {@code b} is {@code null}, 35 | * a {@code NullPointerException} is thrown. 36 | * If {@code b.length} is zero, then 37 | * no bytes are read. Otherwise, the first 38 | * byte read is stored into element {@code b[0]}, 39 | * the next one into {@code b[1]}, and 40 | * so on. 41 | * If an exception is thrown from 42 | * this method, then it may be that some but 43 | * not all bytes of {@code b} have been 44 | * updated with data from the input stream. 45 | * 46 | * @param b the buffer into which the data is read. 47 | * @throws NullPointerException if {@code b} is {@code null}. 48 | * @throws EOFException if this stream reaches the end before reading 49 | * all the bytes. 50 | * @throws IOException if an I/O error occurs. 51 | */ 52 | void readFully(byte b[]) throws IOException; 53 | 54 | /** 55 | * 56 | * Reads {@code len} 57 | * bytes from 58 | * an input stream. 59 | *

60 | * This method 61 | * blocks until one of the following conditions 62 | * occurs: 63 | *

    64 | *
  • {@code len} bytes 65 | * of input data are available, in which case 66 | * a normal return is made. 67 | * 68 | *
  • End of file 69 | * is detected, in which case an {@code EOFException} 70 | * is thrown. 71 | * 72 | *
  • An I/O error occurs, in 73 | * which case an {@code IOException} other 74 | * than {@code EOFException} is thrown. 75 | *
76 | *

77 | * If {@code b} is {@code null}, 78 | * a {@code NullPointerException} is thrown. 79 | * If {@code off} is negative, or {@code len} 80 | * is negative, or {@code off+len} is 81 | * greater than the length of the array {@code b}, 82 | * then an {@code IndexOutOfBoundsException} 83 | * is thrown. 84 | * If {@code len} is zero, 85 | * then no bytes are read. Otherwise, the first 86 | * byte read is stored into element {@code b[off]}, 87 | * the next one into {@code b[off+1]}, 88 | * and so on. The number of bytes read is, 89 | * at most, equal to {@code len}. 90 | * 91 | * @param b the buffer into which the data is read. 92 | * @param off an int specifying the offset in the data array {@code b}. 93 | * @param len an int specifying the number of bytes to read. 94 | * @throws NullPointerException if {@code b} is {@code null}. 95 | * @throws IndexOutOfBoundsException if {@code off} is negative, 96 | * {@code len} is negative, or {@code len} is greater than 97 | * {@code b.length - off}. 98 | * @throws EOFException if this stream reaches the end before reading 99 | * all the bytes. 100 | * @throws IOException if an I/O error occurs. 101 | */ 102 | void readFully(byte b[], int off, int len) throws IOException; 103 | 104 | /** 105 | * Makes an attempt to skip over 106 | * {@code n} bytes 107 | * of data from the input 108 | * stream, discarding the skipped bytes. However, 109 | * it may skip 110 | * over some smaller number of 111 | * bytes, possibly zero. This may result from 112 | * any of a 113 | * number of conditions; reaching 114 | * end of file before {@code n} bytes 115 | * have been skipped is 116 | * only one possibility. 117 | * This method never throws an {@code EOFException}. 118 | * The actual 119 | * number of bytes skipped is returned. 120 | * 121 | * @param n the number of bytes to be skipped. 122 | * @return the number of bytes actually skipped. 123 | * @exception IOException if an I/O error occurs. 124 | */ 125 | int skipBytes(int n) throws IOException; 126 | 127 | /** 128 | * Reads one input byte and returns 129 | * {@code true} if that byte is nonzero, 130 | * {@code false} if that byte is zero. 131 | * This method is suitable for reading 132 | * the byte written by the {@code writeBoolean} 133 | * method of interface {@code DataOutput}. 134 | * 135 | * @return the {@code boolean} value read. 136 | * @exception EOFException if this stream reaches the end before reading 137 | * all the bytes. 138 | * @exception IOException if an I/O error occurs. 139 | */ 140 | boolean readBoolean() throws IOException; 141 | 142 | /** 143 | * Reads and returns one input byte. 144 | * The byte is treated as a signed value in 145 | * the range {@code -128} through {@code 127}, 146 | * inclusive. 147 | * This method is suitable for 148 | * reading the byte written by the {@code writeByte} 149 | * method of interface {@code DataOutput}. 150 | * 151 | * @return the 8-bit value read. 152 | * @exception EOFException if this stream reaches the end before reading 153 | * all the bytes. 154 | * @exception IOException if an I/O error occurs. 155 | */ 156 | byte readByte() throws IOException; 157 | 158 | /** 159 | * Reads one input byte, zero-extends 160 | * it to type {@code int}, and returns 161 | * the result, which is therefore in the range 162 | * {@code 0} 163 | * through {@code 255}. 164 | * This method is suitable for reading 165 | * the byte written by the {@code writeByte} 166 | * method of interface {@code DataOutput} 167 | * if the argument to {@code writeByte} 168 | * was intended to be a value in the range 169 | * {@code 0} through {@code 255}. 170 | * 171 | * @return the unsigned 8-bit value read. 172 | * @exception EOFException if this stream reaches the end before reading 173 | * all the bytes. 174 | * @exception IOException if an I/O error occurs. 175 | */ 176 | int readUnsignedByte() throws IOException; 177 | 178 | /** 179 | * Reads two input bytes and returns 180 | * a {@code short} value. Let {@code a} 181 | * be the first byte read and {@code b} 182 | * be the second byte. The value 183 | * returned 184 | * is: 185 | *

{@code (short)((a << 8) | (b & 0xff))
186 |      * }
187 | * This method 188 | * is suitable for reading the bytes written 189 | * by the {@code writeShort} method of 190 | * interface {@code DataOutput}. 191 | * 192 | * @return the 16-bit value read. 193 | * @exception EOFException if this stream reaches the end before reading 194 | * all the bytes. 195 | * @exception IOException if an I/O error occurs. 196 | */ 197 | short readShort() throws IOException; 198 | 199 | /** 200 | * Reads two input bytes and returns 201 | * an {@code int} value in the range {@code 0} 202 | * through {@code 65535}. Let {@code a} 203 | * be the first byte read and 204 | * {@code b} 205 | * be the second byte. The value returned is: 206 | *
{@code (((a & 0xff) << 8) | (b & 0xff))
207 |      * }
208 | * This method is suitable for reading the bytes 209 | * written by the {@code writeShort} method 210 | * of interface {@code DataOutput} if 211 | * the argument to {@code writeShort} 212 | * was intended to be a value in the range 213 | * {@code 0} through {@code 65535}. 214 | * 215 | * @return the unsigned 16-bit value read. 216 | * @exception EOFException if this stream reaches the end before reading 217 | * all the bytes. 218 | * @exception IOException if an I/O error occurs. 219 | */ 220 | int readUnsignedShort() throws IOException; 221 | 222 | /** 223 | * Reads two input bytes and returns a {@code char} value. 224 | * Let {@code a} 225 | * be the first byte read and {@code b} 226 | * be the second byte. The value 227 | * returned is: 228 | *
{@code (char)((a << 8) | (b & 0xff))
229 |      * }
230 | * This method 231 | * is suitable for reading bytes written by 232 | * the {@code writeChar} method of interface 233 | * {@code DataOutput}. 234 | * 235 | * @return the {@code char} value read. 236 | * @exception EOFException if this stream reaches the end before reading 237 | * all the bytes. 238 | * @exception IOException if an I/O error occurs. 239 | */ 240 | char readChar() throws IOException; 241 | 242 | /** 243 | * Reads four input bytes and returns an 244 | * {@code int} value. Let {@code a-d} 245 | * be the first through fourth bytes read. The value returned is: 246 | *
{@code
247 |      * (((a & 0xff) << 24) | ((b & 0xff) << 16) |
248 |      *  ((c & 0xff) <<  8) | (d & 0xff))
249 |      * }
250 | * This method is suitable 251 | * for reading bytes written by the {@code writeInt} 252 | * method of interface {@code DataOutput}. 253 | * 254 | * @return the {@code int} value read. 255 | * @exception EOFException if this stream reaches the end before reading 256 | * all the bytes. 257 | * @exception IOException if an I/O error occurs. 258 | */ 259 | int readInt() throws IOException; 260 | 261 | /** 262 | * Reads eight input bytes and returns 263 | * a {@code long} value. Let {@code a-h} 264 | * be the first through eighth bytes read. 265 | * The value returned is: 266 | *
{@code
267 |      * (((long)(a & 0xff) << 56) |
268 |      *  ((long)(b & 0xff) << 48) |
269 |      *  ((long)(c & 0xff) << 40) |
270 |      *  ((long)(d & 0xff) << 32) |
271 |      *  ((long)(e & 0xff) << 24) |
272 |      *  ((long)(f & 0xff) << 16) |
273 |      *  ((long)(g & 0xff) <<  8) |
274 |      *  ((long)(h & 0xff)))
275 |      * }
276 | *

277 | * This method is suitable 278 | * for reading bytes written by the {@code writeLong} 279 | * method of interface {@code DataOutput}. 280 | * 281 | * @return the {@code long} value read. 282 | * @exception EOFException if this stream reaches the end before reading 283 | * all the bytes. 284 | * @exception IOException if an I/O error occurs. 285 | */ 286 | long readLong() throws IOException; 287 | 288 | /** 289 | * Reads four input bytes and returns 290 | * a {@code float} value. It does this 291 | * by first constructing an {@code int} 292 | * value in exactly the manner 293 | * of the {@code readInt} 294 | * method, then converting this {@code int} 295 | * value to a {@code float} in 296 | * exactly the manner of the method {@code Float.intBitsToFloat}. 297 | * This method is suitable for reading 298 | * bytes written by the {@code writeFloat} 299 | * method of interface {@code DataOutput}. 300 | * 301 | * @return the {@code float} value read. 302 | * @exception EOFException if this stream reaches the end before reading 303 | * all the bytes. 304 | * @exception IOException if an I/O error occurs. 305 | */ 306 | float readFloat() throws IOException; 307 | 308 | /** 309 | * Reads eight input bytes and returns 310 | * a {@code double} value. It does this 311 | * by first constructing a {@code long} 312 | * value in exactly the manner 313 | * of the {@code readLong} 314 | * method, then converting this {@code long} 315 | * value to a {@code double} in exactly 316 | * the manner of the method {@code Double.longBitsToDouble}. 317 | * This method is suitable for reading 318 | * bytes written by the {@code writeDouble} 319 | * method of interface {@code DataOutput}. 320 | * 321 | * @return the {@code double} value read. 322 | * @exception EOFException if this stream reaches the end before reading 323 | * all the bytes. 324 | * @exception IOException if an I/O error occurs. 325 | */ 326 | double readDouble() throws IOException; 327 | 328 | /** 329 | * Reads the next line of text from the input stream. 330 | * It reads successive bytes, converting 331 | * each byte separately into a character, 332 | * until it encounters a line terminator or 333 | * end of 334 | * file; the characters read are then 335 | * returned as a {@code String}. Note 336 | * that because this 337 | * method processes bytes, 338 | * it does not support input of the classes Unicode 339 | * character set. 340 | *

341 | * If end of file is encountered 342 | * before even one byte can be read, then {@code null} 343 | * is returned. Otherwise, each byte that is 344 | * read is converted to type {@code char} 345 | * by zero-extension. If the character {@code '\n'} 346 | * is encountered, it is discarded and reading 347 | * ceases. If the character {@code '\r'} 348 | * is encountered, it is discarded and, if 349 | * the following byte converts to the 350 | * character {@code '\n'}, then that is 351 | * discarded also; reading then ceases. If 352 | * end of file is encountered before either 353 | * of the characters {@code '\n'} and 354 | * {@code '\r'} is encountered, reading 355 | * ceases. Once reading has ceased, a {@code String} 356 | * is returned that contains all the characters 357 | * read and not discarded, taken in order. 358 | * Note that every character in this string 359 | * will have a value less than {@code \u005Cu0100}, 360 | * that is, {@code (char)256}. 361 | * 362 | * @return the next line of text from the input stream, 363 | * or {@code null} if the end of file is 364 | * encountered before a byte can be read. 365 | * @exception IOException if an I/O error occurs. 366 | */ 367 | String readLine() throws IOException; 368 | 369 | /** 370 | * Reads in a string that has been encoded using a 371 | * modified UTF-8 372 | * format. 373 | * The general contract of {@code readUTF} 374 | * is that it reads a representation of a Unicode 375 | * character string encoded in modified 376 | * UTF-8 format; this string of characters 377 | * is then returned as a {@code String}. 378 | *

379 | * First, two bytes are read and used to 380 | * construct an unsigned 16-bit integer in 381 | * exactly the manner of the {@code readUnsignedShort} 382 | * method . This integer value is called the 383 | * UTF length and specifies the number 384 | * of additional bytes to be read. These bytes 385 | * are then converted to characters by considering 386 | * them in groups. The length of each group 387 | * is computed from the value of the first 388 | * byte of the group. The byte following a 389 | * group, if any, is the first byte of the 390 | * next group. 391 | *

392 | * If the first byte of a group 393 | * matches the bit pattern {@code 0xxxxxxx} 394 | * (where {@code x} means "may be {@code 0} 395 | * or {@code 1}"), then the group consists 396 | * of just that byte. The byte is zero-extended 397 | * to form a character. 398 | *

399 | * If the first byte 400 | * of a group matches the bit pattern {@code 110xxxxx}, 401 | * then the group consists of that byte {@code a} 402 | * and a second byte {@code b}. If there 403 | * is no byte {@code b} (because byte 404 | * {@code a} was the last of the bytes 405 | * to be read), or if byte {@code b} does 406 | * not match the bit pattern {@code 10xxxxxx}, 407 | * then a {@code UTFDataFormatException} 408 | * is thrown. Otherwise, the group is converted 409 | * to the character: 410 | *

{@code (char)(((a & 0x1F) << 6) | (b & 0x3F))
411 |      * }
412 | * If the first byte of a group 413 | * matches the bit pattern {@code 1110xxxx}, 414 | * then the group consists of that byte {@code a} 415 | * and two more bytes {@code b} and {@code c}. 416 | * If there is no byte {@code c} (because 417 | * byte {@code a} was one of the last 418 | * two of the bytes to be read), or either 419 | * byte {@code b} or byte {@code c} 420 | * does not match the bit pattern {@code 10xxxxxx}, 421 | * then a {@code UTFDataFormatException} 422 | * is thrown. Otherwise, the group is converted 423 | * to the character: 424 | *
{@code
425 |      * (char)(((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F))
426 |      * }
427 | * If the first byte of a group matches the 428 | * pattern {@code 1111xxxx} or the pattern 429 | * {@code 10xxxxxx}, then a {@code UTFDataFormatException} 430 | * is thrown. 431 | *

432 | * If end of file is encountered 433 | * at any time during this entire process, 434 | * then an {@code EOFException} is thrown. 435 | *

436 | * After every group has been converted to 437 | * a character by this process, the characters 438 | * are gathered, in the same order in which 439 | * their corresponding groups were read from 440 | * the input stream, to form a {@code String}, 441 | * which is returned. 442 | *

443 | * The {@code writeUTF} 444 | * method of interface {@code DataOutput} 445 | * may be used to write data that is suitable 446 | * for reading by this method. 447 | * @return a Unicode string. 448 | * @exception EOFException if this stream reaches the end 449 | * before reading all the bytes. 450 | * @exception IOException if an I/O error occurs. 451 | * @exception UTFDataFormatException if the bytes do not represent a 452 | * valid modified UTF-8 encoding of a string. 453 | */ 454 | String readUTF() throws IOException; 455 | } 456 | -------------------------------------------------------------------------------- /Implementor/full/interfaces/standard/DataOutput.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.interfaces.standard; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Copy of {@link java.io.DataOutput} 7 | */ 8 | public interface DataOutput { 9 | /** 10 | * Writes to the output stream the eight 11 | * low-order bits of the argument b. 12 | * The 24 high-order bits of b 13 | * are ignored. 14 | * 15 | * @param b the byte to be written. 16 | * @throws IOException if an I/O error occurs. 17 | */ 18 | void write(int b) throws IOException; 19 | 20 | /** 21 | * Writes to the output stream all the bytes in array b. 22 | * If b is null, 23 | * a NullPointerException is thrown. 24 | * If b.length is zero, then 25 | * no bytes are written. Otherwise, the byte 26 | * b[0] is written first, then 27 | * b[1], and so on; the last byte 28 | * written is b[b.length-1]. 29 | * 30 | * @param b the data. 31 | * @throws IOException if an I/O error occurs. 32 | */ 33 | void write(byte b[]) throws IOException; 34 | 35 | /** 36 | * Writes len bytes from array 37 | * b, in order, to 38 | * the output stream. If b 39 | * is null, a NullPointerException 40 | * is thrown. If off is negative, 41 | * or len is negative, or off+len 42 | * is greater than the length of the array 43 | * b, then an IndexOutOfBoundsException 44 | * is thrown. If len is zero, 45 | * then no bytes are written. Otherwise, the 46 | * byte b[off] is written first, 47 | * then b[off+1], and so on; the 48 | * last byte written is b[off+len-1]. 49 | * 50 | * @param b the data. 51 | * @param off the start offset in the data. 52 | * @param len the number of bytes to write. 53 | * @throws IOException if an I/O error occurs. 54 | */ 55 | void write(byte b[], int off, int len) throws IOException; 56 | 57 | /** 58 | * Writes a boolean value to this output stream. 59 | * If the argument v 60 | * is true, the value (byte)1 61 | * is written; if v is false, 62 | * the value (byte)0 is written. 63 | * The byte written by this method may 64 | * be read by the readBoolean 65 | * method of interface DataInput, 66 | * which will then return a boolean 67 | * equal to v. 68 | * 69 | * @param v the boolean to be written. 70 | * @throws IOException if an I/O error occurs. 71 | */ 72 | void writeBoolean(boolean v) throws IOException; 73 | 74 | /** 75 | * Writes to the output stream the eight low- 76 | * order bits of the argument v. 77 | * The 24 high-order bits of v 78 | * are ignored. (This means that writeByte 79 | * does exactly the same thing as write 80 | * for an integer argument.) The byte written 81 | * by this method may be read by the readByte 82 | * method of interface DataInput, 83 | * which will then return a byte 84 | * equal to (byte)v. 85 | * 86 | * @param v the byte value to be written. 87 | * @throws IOException if an I/O error occurs. 88 | */ 89 | void writeByte(int v) throws IOException; 90 | 91 | /** 92 | * Writes two bytes to the output 93 | * stream to represent the value of the argument. 94 | * The byte values to be written, in the order 95 | * shown, are: 96 | *

{@code
 97 |      * (byte)(0xff & (v >> 8))
 98 |      * (byte)(0xff & v)
 99 |      * }

100 | * The bytes written by this method may be 101 | * read by the readShort method 102 | * of interface DataInput , which 103 | * will then return a short equal 104 | * to (short)v. 105 | * 106 | * @param v the short value to be written. 107 | * @throws IOException if an I/O error occurs. 108 | */ 109 | void writeShort(int v) throws IOException; 110 | 111 | /** 112 | * Writes a char value, which 113 | * is comprised of two bytes, to the 114 | * output stream. 115 | * The byte values to be written, in the order 116 | * shown, are: 117 | *

{@code
118 |      * (byte)(0xff & (v >> 8))
119 |      * (byte)(0xff & v)
120 |      * }

121 | * The bytes written by this method may be 122 | * read by the readChar method 123 | * of interface DataInput , which 124 | * will then return a char equal 125 | * to (char)v. 126 | * 127 | * @param v the char value to be written. 128 | * @throws IOException if an I/O error occurs. 129 | */ 130 | void writeChar(int v) throws IOException; 131 | 132 | /** 133 | * Writes an int value, which is 134 | * comprised of four bytes, to the output stream. 135 | * The byte values to be written, in the order 136 | * shown, are: 137 | *

{@code
138 |      * (byte)(0xff & (v >> 24))
139 |      * (byte)(0xff & (v >> 16))
140 |      * (byte)(0xff & (v >>  8))
141 |      * (byte)(0xff & v)
142 |      * }

143 | * The bytes written by this method may be read 144 | * by the readInt method of interface 145 | * DataInput , which will then 146 | * return an int equal to v. 147 | * 148 | * @param v the int value to be written. 149 | * @throws IOException if an I/O error occurs. 150 | */ 151 | void writeInt(int v) throws IOException; 152 | 153 | /** 154 | * Writes a long value, which is 155 | * comprised of eight bytes, to the output stream. 156 | * The byte values to be written, in the order 157 | * shown, are: 158 | *

{@code
159 |      * (byte)(0xff & (v >> 56))
160 |      * (byte)(0xff & (v >> 48))
161 |      * (byte)(0xff & (v >> 40))
162 |      * (byte)(0xff & (v >> 32))
163 |      * (byte)(0xff & (v >> 24))
164 |      * (byte)(0xff & (v >> 16))
165 |      * (byte)(0xff & (v >>  8))
166 |      * (byte)(0xff & v)
167 |      * }

168 | * The bytes written by this method may be 169 | * read by the readLong method 170 | * of interface DataInput , which 171 | * will then return a long equal 172 | * to v. 173 | * 174 | * @param v the long value to be written. 175 | * @throws IOException if an I/O error occurs. 176 | */ 177 | void writeLong(long v) throws IOException; 178 | 179 | /** 180 | * Writes a float value, 181 | * which is comprised of four bytes, to the output stream. 182 | * It does this as if it first converts this 183 | * float value to an int 184 | * in exactly the manner of the Float.floatToIntBits 185 | * method and then writes the int 186 | * value in exactly the manner of the writeInt 187 | * method. The bytes written by this method 188 | * may be read by the readFloat 189 | * method of interface DataInput, 190 | * which will then return a float 191 | * equal to v. 192 | * 193 | * @param v the float value to be written. 194 | * @throws IOException if an I/O error occurs. 195 | */ 196 | void writeFloat(float v) throws IOException; 197 | 198 | /** 199 | * Writes a double value, 200 | * which is comprised of eight bytes, to the output stream. 201 | * It does this as if it first converts this 202 | * double value to a long 203 | * in exactly the manner of the Double.doubleToLongBits 204 | * method and then writes the long 205 | * value in exactly the manner of the writeLong 206 | * method. The bytes written by this method 207 | * may be read by the readDouble 208 | * method of interface DataInput, 209 | * which will then return a double 210 | * equal to v. 211 | * 212 | * @param v the double value to be written. 213 | * @throws IOException if an I/O error occurs. 214 | */ 215 | void writeDouble(double v) throws IOException; 216 | 217 | /** 218 | * Writes a string to the output stream. 219 | * For every character in the string 220 | * s, taken in order, one byte 221 | * is written to the output stream. If 222 | * s is null, a NullPointerException 223 | * is thrown.

If s.length 224 | * is zero, then no bytes are written. Otherwise, 225 | * the character s[0] is written 226 | * first, then s[1], and so on; 227 | * the last character written is s[s.length-1]. 228 | * For each character, one byte is written, 229 | * the low-order byte, in exactly the manner 230 | * of the writeByte method . The 231 | * high-order eight bits of each character 232 | * in the string are ignored. 233 | * 234 | * @param s the string of bytes to be written. 235 | * @throws IOException if an I/O error occurs. 236 | */ 237 | void writeBytes(String s) throws IOException; 238 | 239 | /** 240 | * Writes every character in the string s, 241 | * to the output stream, in order, 242 | * two bytes per character. If s 243 | * is null, a NullPointerException 244 | * is thrown. If s.length 245 | * is zero, then no characters are written. 246 | * Otherwise, the character s[0] 247 | * is written first, then s[1], 248 | * and so on; the last character written is 249 | * s[s.length-1]. For each character, 250 | * two bytes are actually written, high-order 251 | * byte first, in exactly the manner of the 252 | * writeChar method. 253 | * 254 | * @param s the string value to be written. 255 | * @throws IOException if an I/O error occurs. 256 | */ 257 | void writeChars(String s) throws IOException; 258 | 259 | /** 260 | * Writes two bytes of length information 261 | * to the output stream, followed 262 | * by the 263 | * modified UTF-8 264 | * representation 265 | * of every character in the string s. 266 | * If s is null, 267 | * a NullPointerException is thrown. 268 | * Each character in the string s 269 | * is converted to a group of one, two, or 270 | * three bytes, depending on the value of the 271 | * character.

272 | * If a character c 273 | * is in the range \u0001 through 274 | * \u007f, it is represented 275 | * by one byte: 276 | *

(byte)c 

277 | * If a character c is \u0000 278 | * or is in the range \u0080 279 | * through \u07ff, then it is 280 | * represented by two bytes, to be written 281 | * in the order shown:

{@code
282 |      * (byte)(0xc0 | (0x1f & (c >> 6)))
283 |      * (byte)(0x80 | (0x3f & c))
284 |      * }

If a character 285 | * c is in the range \u0800 286 | * through uffff, then it is 287 | * represented by three bytes, to be written 288 | * in the order shown:

{@code
289 |      * (byte)(0xe0 | (0x0f & (c >> 12)))
290 |      * (byte)(0x80 | (0x3f & (c >>  6)))
291 |      * (byte)(0x80 | (0x3f & c))
292 |      * }

First, 293 | * the total number of bytes needed to represent 294 | * all the characters of s is 295 | * calculated. If this number is larger than 296 | * 65535, then a UTFDataFormatException 297 | * is thrown. Otherwise, this length is written 298 | * to the output stream in exactly the manner 299 | * of the writeShort method; 300 | * after this, the one-, two-, or three-byte 301 | * representation of each character in the 302 | * string s is written.

The 303 | * bytes written by this method may be read 304 | * by the readUTF method of interface 305 | * DataInput , which will then 306 | * return a String equal to s. 307 | * 308 | * @param s the string value to be written. 309 | * @throws IOException if an I/O error occurs. 310 | */ 311 | void writeUTF(String s) throws IOException; 312 | } 313 | -------------------------------------------------------------------------------- /Implementor/full/interfaces/standard/SDeprecated.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.interfaces.standard; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | import static java.lang.annotation.ElementType.*; 9 | 10 | /** 11 | * Copy of {@link Deprecated} 12 | */ 13 | @Documented 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) 16 | public @interface SDeprecated { 17 | /** 18 | * Returns the version in which the annotated element became deprecated. 19 | * The version string is in the same format and namespace as the value of 20 | * the {@code @since} javadoc tag. The default value is the empty 21 | * string. 22 | * 23 | * @return the version string 24 | * @since 9 25 | */ 26 | String since() default ""; 27 | 28 | /** 29 | * Indicates whether the annotated element is subject to removal in a 30 | * future version. The default value is {@code false}. 31 | * 32 | * @return whether the element is subject to removal 33 | * @since 9 34 | */ 35 | boolean forRemoval() default false; 36 | } 37 | -------------------------------------------------------------------------------- /Implementor/full/interfaces/standard/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Standard interfaces for tests 3 | * of Implementor homework 4 | * for Java Advanced course. 5 | * 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | package info.kgeorgiy.java.advanced.implementor.full.interfaces.standard; -------------------------------------------------------------------------------- /Implementor/full/lang/Arabic.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.lang; 2 | 3 | /** 4 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 5 | */ 6 | public abstract class Arabic { 7 | public Arabic(\u0645\u0631\u062d\u0628\u0627 \u0645\u0631\u062d\u0628\u0627) throws \u0645\u0631\u062d\u0628\u0627 { 8 | } 9 | 10 | public abstract \u0645\u0631\u062d\u0628\u0627 \u0645\u0631\u062d\u0628\u0627(\u0645\u0631\u062d\u0628\u0627 \u0645\u0631\u062d\u0628\u0627) throws \u0645\u0631\u062d\u0628\u0627; 11 | 12 | public static class \u0645\u0631\u062d\u0628\u0627 extends Exception { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Implementor/full/lang/Greek.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.lang; 2 | 3 | /** 4 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 5 | */ 6 | public abstract class Greek { 7 | public Greek(\u03b3\u03b5\u03b9\u03b1 \u03b3\u03b5\u03b9\u03b1) throws \u03b3\u03b5\u03b9\u03b1 { 8 | } 9 | 10 | public abstract \u03b3\u03b5\u03b9\u03b1 \u03b3\u03b5\u03b9\u03b1(\u03b3\u03b5\u03b9\u03b1 \u03b3\u03b5\u03b9\u03b1) throws \u03b3\u03b5\u03b9\u03b1; 11 | 12 | public static class \u03b3\u03b5\u03b9\u03b1 extends Exception { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Implementor/full/lang/Hebrew.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.lang; 2 | 3 | /** 4 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 5 | */ 6 | public abstract class Hebrew { 7 | public Hebrew(\u05d4\u05d9 \u05d4\u05d9) throws \u05d4\u05d9 { 8 | } 9 | 10 | public abstract \u05d4\u05d9 \u05d4\u05d9(\u05d4\u05d9 \u05d4\u05d9) throws \u05d4\u05d9; 11 | 12 | public static class \u05d4\u05d9 extends Exception { 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Implementor/full/lang/Russian.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.lang; 2 | 3 | /** 4 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 5 | */ 6 | public abstract class Russian implements \u041f\u0440\u0438\u0432\u0435\u0442Interface { 7 | public Russian(\u041f\u0440\u0438\u0432\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0442) throws \u041f\u0440\u0438\u0432\u0435\u0442 { 8 | } 9 | 10 | public abstract \u041f\u0440\u0438\u0432\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0442(\u041f\u0440\u0438\u0432\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0442) throws \u041f\u0440\u0438\u0432\u0435\u0442; 11 | 12 | public static class \u041f\u0440\u0438\u0432\u0435\u0442 extends Exception { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Implementor/full/lang/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Internationalized classes for tests 3 | * of Implementor homework 4 | * for Java Advanced course. 5 | * 6 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 7 | */ 8 | package info.kgeorgiy.java.advanced.implementor.full.lang; -------------------------------------------------------------------------------- /Implementor/full/lang/ПриветInterface.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.java.advanced.implementor.full.lang; 2 | 3 | public interface \u041f\u0440\u0438\u0432\u0435\u0442Interface { 4 | } 5 | -------------------------------------------------------------------------------- /NavigableSetTest.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.ja.фамилия.arrayset; 2 | 3 | import net.java.quickcheck.collection.Pair; 4 | import org.junit.Assert; 5 | import org.junit.FixMethodOrder; 6 | import org.junit.Test; 7 | import org.junit.runners.MethodSorters; 8 | 9 | import java.util.*; 10 | import java.util.function.Function; 11 | 12 | import static net.java.quickcheck.generator.CombinedGeneratorsIterables.somePairs; 13 | import static net.java.quickcheck.generator.PrimitiveGenerators.fixedValues; 14 | import static org.junit.Assert.assertEquals; 15 | 16 | /** 17 | * Tests for hard version 18 | * of ArraySet homework 19 | * for Java Advanced course. 20 | * 21 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 22 | */ 23 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 24 | public class NavigableSetTest extends SortedSetTest { 25 | @Test 26 | public void test31_lower() { 27 | for (final Pair> pair : withComparator()) { 28 | final List elements = pair.getSecond(); 29 | final Comparator comparator = pair.getFirst(); 30 | final NavigableSet set = set(elements, comparator); 31 | final NavigableSet treeSet = treeSet(elements, comparator); 32 | 33 | for (final Integer element : inAndOut(elements)) { 34 | assertEquals( 35 | "in lower(" + element + ") (comparator = " + comparator + ", elements = " + elements + ")", 36 | treeSet.lower(element), 37 | set.lower(element) 38 | ); 39 | } 40 | } 41 | } 42 | 43 | @Test 44 | public void test32_ceiling() { 45 | for (final Pair> pair : withComparator()) { 46 | final List elements = pair.getSecond(); 47 | final Comparator comparator = pair.getFirst(); 48 | final NavigableSet set = set(elements, comparator); 49 | final NavigableSet treeSet = treeSet(elements, comparator); 50 | 51 | for (final Integer element : inAndOut(elements)) { 52 | assertEquals( 53 | "in " + "ceiling" + "(" + element + ") (comparator = " + comparator + ", elements = " + elements + ")", 54 | treeSet.ceiling(element), 55 | set.ceiling(element) 56 | ); 57 | } 58 | } 59 | } 60 | 61 | @Test 62 | public void test33_higher() { 63 | for (final Pair> pair : withComparator()) { 64 | final List elements = pair.getSecond(); 65 | final Comparator comparator = pair.getFirst(); 66 | final NavigableSet set = set(elements, comparator); 67 | final NavigableSet treeSet = treeSet(elements, comparator); 68 | 69 | for (final Integer element : inAndOut(elements)) { 70 | assertEquals( 71 | "in " + "higher(" + element + ") (comparator = " + comparator + ", elements = " + elements + ")", 72 | treeSet.higher(element), 73 | set.higher(element) 74 | ); 75 | } 76 | } 77 | } 78 | 79 | @Test 80 | public void test34_floor() { 81 | for (final Pair> pair : withComparator()) { 82 | final List elements = pair.getSecond(); 83 | final Comparator comparator = pair.getFirst(); 84 | final NavigableSet set = set(elements, comparator); 85 | final NavigableSet treeSet = treeSet(elements, comparator); 86 | 87 | for (final Integer element : inAndOut(elements)) { 88 | assertEquals( 89 | "in floor(" + element + ") (comparator = " + comparator + ", elements = " + elements + ")", 90 | treeSet.floor(element), 91 | set.floor(element) 92 | ); 93 | } 94 | } 95 | } 96 | 97 | @Test 98 | public void test35_navigableTailSet() { 99 | for (final Pair> pair : withComparator()) { 100 | final List elements = pair.getSecond(); 101 | final Comparator comparator = pair.getFirst(); 102 | final NavigableSet set = set(elements, comparator); 103 | final NavigableSet treeSet = treeSet(elements, comparator); 104 | 105 | for (final Integer element : inAndOut(elements)) { 106 | assertEq( 107 | set.tailSet(element, true), 108 | treeSet.tailSet(element, true), 109 | "in tailSet(" + element + ", true) (comparator = " + comparator + ", elements = " + elements + ")" 110 | ); 111 | assertEq( 112 | set.tailSet(element, false), 113 | treeSet.tailSet(element, false), 114 | "in tailSet(" + element + ", false) (comparator = " + comparator + ", elements = " + elements + ")" 115 | ); 116 | } 117 | } 118 | } 119 | 120 | @Test 121 | public void test36_navigableHeadSet() { 122 | for (final Pair> pair : withComparator()) { 123 | final List elements = pair.getSecond(); 124 | final Comparator comparator = pair.getFirst(); 125 | final NavigableSet set = set(elements, comparator); 126 | final NavigableSet treeSet = treeSet(elements, comparator); 127 | 128 | for (final Integer element : inAndOut(elements)) { 129 | assertEq( 130 | set.headSet(element, true), 131 | treeSet.headSet(element, true), 132 | "in headSet(" + element + ", true) (comparator = " + comparator + ", elements = " + elements + ")" 133 | ); 134 | assertEq( 135 | set.headSet(element, false), 136 | treeSet.headSet(element, false), 137 | "in headSet(" + element + ", false) (comparator = " + comparator + ", elements = " + elements + ")" 138 | ); 139 | } 140 | } 141 | } 142 | 143 | @Test 144 | public void test37_navigableSubSet() { 145 | for (final Pair> pair : withComparator()) { 146 | final List elements = pair.getSecond(); 147 | final Comparator comparator = pair.getFirst() != null ? pair.getFirst() : Comparator.naturalOrder(); 148 | final NavigableSet set = set(elements, pair.getFirst()); 149 | final NavigableSet treeSet = treeSet(elements, pair.getFirst()); 150 | 151 | final Collection all = values(elements); 152 | for (final Pair p : somePairs(fixedValues(all), fixedValues(all))) { 153 | final Integer from = p.getFirst(); 154 | final Integer to = p.getSecond(); 155 | if (comparator.compare(from, to) <= 0) { 156 | for (int i = 0; i < 4; i++) { 157 | assertEq( 158 | set.subSet(from, i % 2 == 1, to, i / 2 == 1), 159 | treeSet.subSet(from, i % 2 == 1, to, i / 2 == 1), 160 | String.format("in subSet(%d, %b, %d, %b) (comparator = %s, elements = %s", 161 | from, i % 2 == 1, 162 | to, i / 2 == 1, 163 | comparator, elements 164 | ) 165 | ); 166 | } 167 | } 168 | } 169 | } 170 | } 171 | 172 | @Test 173 | public void test38_mutators() { 174 | final NavigableSet set = set(List.of(1, 2, 3), Integer::compareTo); 175 | testMutator(set::pollFirst); 176 | testMutator(set::pollLast); 177 | } 178 | 179 | @Test 180 | public void test39_descendingSet() { 181 | testDescendingSet(set(List.of(10, 20, 30), Integer::compareTo).descendingSet()); 182 | testDescendingSet(set(List.of(10, 20, 30), null).descendingSet()); 183 | } 184 | 185 | private static void testDescendingSet(final NavigableSet set) { 186 | assertEquals("toArray()", List.of(30, 20, 10), toArray(set)); 187 | assertEquals("size()", 3, set.size()); 188 | assertEquals("first()", 30, set.first().intValue()); 189 | assertEquals("last()", 10, set.last().intValue()); 190 | assertEquals("descendingIterator().next()", 10, set.descendingIterator().next().intValue()); 191 | 192 | testGet("floor(%s)", set::floor, descendingPairs(10, 10, 20, 20, 30, 30, null)); 193 | testGet("lower(%s)", set::lower, descendingPairs(10, 20, 20, 30, 30, null, null)); 194 | testGet("ceiling(%s)", set::ceiling, descendingPairs(null, 10, 10, 20, 20, 30, 30)); 195 | testGet("higher(%s)", set::higher, descendingPairs(null, null, 10, 10, 20, 20, 30)); 196 | 197 | testGet("headSet(%s).size()", i -> set.headSet(i).size(), descendingPairs(3, 2, 2, 1, 1, 0, 0)); 198 | testGet("tailSet(%s).size()", i -> set.tailSet(i).size(), descendingPairs(0, 1, 1, 2, 2, 3, 3)); 199 | assertEquals("descendingSet().toArray()", List.of(10, 20, 30), toArray(set.descendingSet())); 200 | } 201 | 202 | private static List> descendingPairs(final Integer v5, final Integer v10, final Integer v15, final Integer v20, final Integer v25, final Integer v30, final Integer v35) { 203 | return List.of( 204 | pair(5, v5), 205 | pair(10, v10), 206 | pair(15, v15), 207 | pair(20, v20), 208 | pair(25, v25), 209 | pair(30, v30), 210 | pair(35, v35) 211 | ); 212 | } 213 | 214 | private static void testGet(final String format, final Function method, final List> pairs) { 215 | for (final Pair pair : pairs) { 216 | assertEquals(String.format(format, pair.getFirst()), pair.getSecond(), method.apply(pair.getFirst())); 217 | } 218 | } 219 | 220 | private static Pair pair(final T arg, final T result) { 221 | return new Pair<>(arg, result); 222 | } 223 | 224 | protected static void testMutator(final Runnable mutator) { 225 | try { 226 | mutator.run(); 227 | Assert.fail("Expected UnsupportedOperationException"); 228 | } catch (final UnsupportedOperationException ignore) { 229 | // Passed 230 | } 231 | } 232 | 233 | protected NavigableSet set(final List elements, final Comparator comparator) { 234 | return (NavigableSet) super.set(elements, comparator); 235 | } 236 | 237 | protected NavigableSet treeSet(final List elements, final Comparator comparator) { 238 | return (NavigableSet) super.treeSet(elements, comparator); 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /OldGroupQueryTest.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.ja.фамилия.student; 2 | 3 | import info.kgeorgiy.java.advanced.student.Group; 4 | import info.kgeorgiy.java.advanced.student.GroupName; 5 | import info.kgeorgiy.java.advanced.student.GroupQuery; 6 | import info.kgeorgiy.java.advanced.student.Student; 7 | import org.junit.FixMethodOrder; 8 | import org.junit.Test; 9 | import org.junit.runners.MethodSorters; 10 | 11 | import java.util.*; 12 | 13 | /** 14 | * Tests for hard version 15 | * of Student homework 16 | * for Java Advanced course. 17 | * 18 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 19 | */ 20 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 21 | public class OldGroupQueryTest extends OldStudentQueryTest implements GroupQuery { 22 | protected final GroupQuery db = createCUT(); 23 | 24 | @Test 25 | public void test21_testGetGroupsByName() { 26 | test(this::getGroupsByName, db::getGroupsByName); 27 | } 28 | 29 | @Test 30 | public void test22_testGetGroupsById() { 31 | test(this::getGroupsById, db::getGroupsById); 32 | } 33 | 34 | @Test 35 | public void test23_testGetLargestGroup() { 36 | test(this::getLargestGroup, db::getLargestGroup); 37 | } 38 | 39 | @Test 40 | public void test24_testGetLargestGroupByFirstName() { 41 | test(this::getLargestGroupFirstName, db::getLargestGroupFirstName); 42 | } 43 | 44 | // Reference implementation follows 45 | // This implementation is intentionally poorly-written and contains a lot of copy-and-paste 46 | 47 | @Override 48 | public List getGroupsByName(final Collection students) { 49 | final Map groups = new TreeMap<>(); 50 | for (final Student student : students) { 51 | groups.computeIfAbsent(student.getGroup(), group -> new Group(group, findStudentsByGroup(students, group))); 52 | } 53 | return List.copyOf(groups.values()); 54 | } 55 | 56 | @Override 57 | public List getGroupsById(final Collection students) { 58 | final Map groups = new TreeMap<>(); 59 | for (final Student student : students) { 60 | groups.computeIfAbsent(student.getGroup(), group -> { 61 | final ArrayList result = new ArrayList<>(students); 62 | result.removeIf(s -> !s.getGroup().equals(group)); 63 | Collections.sort(result); 64 | return new Group(group, result); 65 | }); 66 | } 67 | return List.copyOf(groups.values()); 68 | } 69 | 70 | @Override 71 | public GroupName getLargestGroup(final Collection students) { 72 | if (students.size() == 0) { 73 | return null; 74 | } 75 | 76 | int maxSize = -1; 77 | GroupName name = GroupName.M3234; 78 | for (final Group group : getGroupsByName(students)) { 79 | if (maxSize < group.getStudents().size() || maxSize == group.getStudents().size() && name.compareTo(group.getName()) < 0) { 80 | maxSize = group.getStudents().size(); 81 | name = group.getName(); 82 | } 83 | } 84 | return name; 85 | } 86 | 87 | @Override 88 | public GroupName getLargestGroupFirstName(final Collection students) { 89 | if (students.size() == 0) { 90 | return null; 91 | } 92 | 93 | int maxSize = -1; 94 | GroupName name = GroupName.M3239; 95 | for (final Group group : getGroupsByName(students)) { 96 | final Set firstNames = new HashSet<>(); 97 | group.getStudents().forEach(student -> firstNames.add(student.getFirstName())); 98 | if (maxSize < firstNames.size() || maxSize == firstNames.size() && name.compareTo(group.getName()) > 0) { 99 | maxSize = firstNames.size(); 100 | name = group.getName(); 101 | } 102 | } 103 | return name; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /OldRecursiveWalkTest.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.ja.фамилия.walk; 2 | 3 | import org.junit.FixMethodOrder; 4 | import org.junit.Test; 5 | import org.junit.runners.MethodSorters; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.Path; 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.Map; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | * Tests for hard version 16 | * of Walk homework 17 | * for Java Advanced course. 18 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 19 | */ 20 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 21 | public class OldRecursiveWalkTest extends OldWalkTest { 22 | @Test 23 | public void test70_singleRecursion() throws IOException { 24 | final Path root = DIR.resolve(name.getMethodName()); 25 | test(Collections.singletonList(root.toString()), randomDirs(3, 4, 100, root)); 26 | } 27 | 28 | @Test 29 | public void test80_doubleRecursion() throws IOException { 30 | final Path root = DIR.resolve(name.getMethodName()); 31 | final Path dir1 = root.resolve(randomFileName()); 32 | final Path dir2 = root.resolve(randomFileName()); 33 | final String from = dir1.toString(); 34 | final String to = dir2.resolve("..").resolve(dir1.getFileName()).toString(); 35 | 36 | final Map files = randomDirs(3, 4, 100, dir1); 37 | files.putAll(files.entrySet().stream().collect(Collectors.toMap(e -> e.getKey().replace(from, to), Map.Entry::getValue))); 38 | files.putAll(randomDirs(3, 4, 100, dir2)); 39 | 40 | test(Arrays.asList(from, dir2.toString(), to), files); 41 | } 42 | 43 | private Map randomDirs(final int n, final int d, final int maxL, final Path dir) throws IOException { 44 | final Map result = randomFiles(RANDOM.nextInt(n + 1), maxL, dir); 45 | if (d > 0) { 46 | for (int i = RANDOM.nextInt(n + 1); i < n; i++) { 47 | result.putAll(randomDirs(n, d - 1, maxL, dir.resolve(randomFileName()))); 48 | } 49 | } 50 | return result; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /OldStudentQueryTest.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.ja.фамилия.student; 2 | 3 | import info.kgeorgiy.java.advanced.base.BaseTest; 4 | import info.kgeorgiy.java.advanced.student.GroupName; 5 | import info.kgeorgiy.java.advanced.student.Student; 6 | import info.kgeorgiy.java.advanced.student.StudentQuery; 7 | import org.junit.Assert; 8 | import org.junit.FixMethodOrder; 9 | import org.junit.Test; 10 | import org.junit.runners.MethodSorters; 11 | 12 | import java.util.*; 13 | import java.util.function.BiFunction; 14 | import java.util.function.BinaryOperator; 15 | import java.util.function.Function; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.IntStream; 18 | import java.util.stream.Stream; 19 | 20 | /** 21 | * Tests for easy version 22 | * of Student homework 23 | * for Java Advanced course. 24 | * 25 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 26 | */ 27 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 28 | public class OldStudentQueryTest extends BaseTest implements StudentQuery { 29 | protected static final Random RANDOM = new Random(8458243580324875285L); 30 | private static final List FIRST_NAMES = List.of("DK3!8", "Азат", "Александр", "Александра", "Алексей", "Анастасия", "Анатолий", "Андрей", "Анна", "Антон", "Арсений", "Артем", "Аслан", "Ася", "Благой", "Борис", "Валерия", "Варвара", "Виктор", "Виталий", "Владислав", "Всеволод", "Вячеслав", "Георгий", "Глеб", "Григорий", "Даниил", "Данил", "Дарья", "Демид", "Демьян", "Денис", "Дмитрий", "Евгений", "Егор", "Елизавета", "Захар", "Зоя", "Ибрагим", "Иван", "Идрис", "Илона", "Ильдар", "Илья", "Ирина", "Кирилл", "Константин", "Любовь", "Мадияр", "Максим", "Марина", "Марк", "Михаил", "Мухаммаджон", "Наталья", "Никита", "Николай", "Олег", "Олеся", "Петр", "Равиль", "Рамазан", "Роман", "Руслан", "Семён", "Сергей", "Станислав", "Тарас", "Темирлан", "Тимофей", "Элина", "Эрика", "Юлия", "Юрий", "Ян", "Янис", "Ярослав" 31 | ); 32 | private static final List LAST_NAMES = List.of("Пивчанский", "Агеев", "Акимов", "Аксенов", "Ашихмин", "Бадикова", "Баженов", "Барляев", "Батырова", "Бобров", "Богер", "Боже", "Бочарников", "Бричев", "Бусыгин", "Бусюк", "Варфоломеев", "Власова", "Вознов", "Воробьев", "Галиев", "Глазов", "Гнатюк", "Голиков", "Градобоев", "Гречишкина", "Грибанов", "Гунькин", "Даниловский", "Деляну", "Джиблави", "Димитров", "Должанский", "Дорофеев", "Дроздов", "Дунидин", "Елфимов", "Жарский", "Желенский", "Желобкович", "Жуверцев", "Жуков", "Загретдинов", "Иванова", "Ившин", "Исаев", "Исаева", "Каримов", "Карлина", "Карлукова", "Кирпичев", "Клёпов", "Ковешников", "Коновалов", "Коробков", "Королев", "Кочергин", "Краснощеков", "Крухмалев", "Крылов", "Кубанцев", "Кузнецов", "Кузьминов", "Кулиев", "Курбатов", "Кухтикова", "Лаптев", "Ларионов", "Лебедев", "Левашов", "Лемешкова", "Луконин", "Любавина", "Максимов", "Мальцев", "Маслов", "Можевитин", "Мозжевилов", "Мошников", "Наумов", "Неверов", "Николаева", "Нургазин", "Осадчий", "Ота", "Отт", "Пак", "Панкратов", "Паночевных", "Первеев", "Потапов", "Пренас", "Рахматуллин", "Рашо", "Резниченко", "Романенко", "Роскошный", "Сакулина", "Селиверстов", "Сердюков", "Симонович", "Скаженик", "Скрыль", "Смирнов", "Стародубцев", "Сухарев", "Тананыкин", "Тарасов", "Теблоев", "Тептин", "Тер-Матевосян", "Тимербулатов", "Тимофеев", "Тихонов", "Ткач", "Ткаченко", "Толепбек", "Третьякова", "Трибрат", "Усс", "Фадеев", "Федоров", "Фролова", "Хакимов", "Хлытин", "Цуциев", "Чеботарева", "Чежегов", "Шаблов", "Шалдин", "Шапошников", "Шарепо", "Шефер", "Шишкин", "Шлапко", "Яндаров", "Ярлыченко" 33 | ); 34 | private static final List GROUPS = List.of(GroupName.M3234, GroupName.M3235, GroupName.M3236, GroupName.M3237, GroupName.M3238, GroupName.M3239); 35 | 36 | public static final int COUNT = 300; 37 | protected static final List STUDENTS = Stream.generate(() -> new Student( 38 | RANDOM.nextInt(), 39 | random(FIRST_NAMES), 40 | random(LAST_NAMES), 41 | random(GROUPS) 42 | )) 43 | .limit(COUNT) 44 | .collect(Collectors.toUnmodifiableList()); 45 | 46 | private static final List> INPUTS = IntStream.range(0, STUDENTS.size()) 47 | .mapToObj(size -> { 48 | final List students = new ArrayList<>(STUDENTS); 49 | Collections.shuffle(students, RANDOM); 50 | return new ArrayList<>(students.subList(0, size)); 51 | }) 52 | .collect(Collectors.toList()); 53 | 54 | private static T random(final List values) { 55 | return values.get(RANDOM.nextInt(values.size())); 56 | } 57 | 58 | private final StudentQuery db = createCUT(); 59 | 60 | @Test 61 | public void test01_testGetFirstNames() { 62 | test(this::getFirstNames, db::getFirstNames); 63 | } 64 | 65 | @Test 66 | public void test02_testGetLastNames() { 67 | test(this::getLastNames, db::getLastNames); 68 | } 69 | 70 | @Test 71 | public void test03_testGetGroups() { 72 | test(this::getGroups, db::getGroups); 73 | } 74 | 75 | @Test 76 | public void test04_testGetFullNames() { 77 | test(this::getFullNames, db::getFullNames); 78 | } 79 | 80 | @Test 81 | public void test05_testGetDistinctFirstNames() { 82 | test(this::getDistinctFirstNames, db::getDistinctFirstNames); 83 | } 84 | 85 | @Test 86 | public void test06_testGetMinStudentFirstName() { 87 | test(this::getMaxStudentFirstName, db::getMaxStudentFirstName); 88 | } 89 | 90 | @Test 91 | public void test07_testSortStudentsById() { 92 | test(this::sortStudentsById, db::sortStudentsById); 93 | } 94 | 95 | @Test 96 | public void test08_testSortStudentsByName() { 97 | test(this::sortStudentsByName, db::sortStudentsByName); 98 | } 99 | 100 | @Test 101 | public void test09_testFindStudentsByFirstName() { 102 | testBi(this::findStudentsByFirstName, db::findStudentsByFirstName, FIRST_NAMES); 103 | } 104 | 105 | @Test 106 | public void test10_testFindStudentsByLastName() { 107 | testBi(this::findStudentsByLastName, db::findStudentsByLastName, FIRST_NAMES); 108 | } 109 | 110 | @Test 111 | public void test11_testFindStudentsByGroup() { 112 | testBi(this::findStudentsByGroup, db::findStudentsByGroup, GROUPS); 113 | } 114 | 115 | @Test 116 | public void test12_findStudentNamesByGroup() { 117 | testBi(this::findStudentNamesByGroupList, db::findStudentNamesByGroupList, GROUPS); 118 | } 119 | 120 | public static void test(final Function, R> reference, final Function, R> tested) { 121 | for (final List input : INPUTS) { 122 | final R actual = tested.apply(input); 123 | Assert.assertEquals("For " + input, reference.apply(input), actual); 124 | } 125 | } 126 | 127 | protected static void testBi( 128 | final BiFunction, U, T> reference, 129 | final BiFunction, U, T> tested, 130 | final List values 131 | ) { 132 | for (final U value : values.subList(0, Math.min(values.size(), 10))) { 133 | System.err.println("\tTesting " + value); 134 | try { 135 | test(input -> reference.apply(input, value), input -> tested.apply(input, value)); 136 | } catch (final AssertionError e) { 137 | throw new AssertionError("Value " + value + ": " + e.getMessage(), e); 138 | } 139 | } 140 | } 141 | 142 | // Reference implementation follows 143 | // This implementation is intentionally poorly-written and contains a lot of copy-and-paste 144 | 145 | @Override 146 | public List getFirstNames(final List students) { 147 | final List result = new ArrayList<>(); 148 | for (final Student student : students) { 149 | result.add(student.getFirstName()); 150 | } 151 | return result; 152 | } 153 | 154 | @Override 155 | public List getLastNames(final List students) { 156 | final List result = new ArrayList<>(); 157 | for (final Student student : students) { 158 | result.add(student.getLastName()); 159 | } 160 | return result; 161 | } 162 | 163 | @Override 164 | public List getGroups(final List students) { 165 | final List result = new ArrayList<>(); 166 | for (final Student student : students) { 167 | result.add(student.getGroup()); 168 | } 169 | return result; 170 | } 171 | 172 | @Override 173 | public List getFullNames(final List students) { 174 | final List result = new ArrayList<>(); 175 | for (final Student student : students) { 176 | result.add(student.getFirstName() + " " + student.getLastName()); 177 | } 178 | return result; 179 | } 180 | 181 | @Override 182 | public Set getDistinctFirstNames(final List students) { 183 | return new TreeSet<>(getFirstNames(students)); 184 | } 185 | 186 | @Override 187 | public String getMaxStudentFirstName(List students) { 188 | int maxId = Integer.MIN_VALUE; 189 | String maxName = ""; 190 | for (final Student student : students) { 191 | if (maxId < student.getId()) { 192 | maxId = student.getId(); 193 | maxName = student.getFirstName(); 194 | } 195 | } 196 | return maxName; 197 | } 198 | 199 | @Override 200 | public List sortStudentsById(final Collection students) { 201 | final ArrayList sorted = new ArrayList<>(students); 202 | Collections.sort(sorted); 203 | return sorted; 204 | } 205 | 206 | private static final Comparator STUDENT_COMPARATOR = 207 | Comparator.comparing(Student::getLastName) 208 | .thenComparing(Student::getFirstName).reversed() 209 | .thenComparing(Student::compareTo); 210 | 211 | @Override 212 | public List sortStudentsByName(final Collection students) { 213 | final ArrayList sorted = new ArrayList<>(students); 214 | sorted.sort(STUDENT_COMPARATOR); 215 | return sorted; 216 | } 217 | 218 | @Override 219 | public List findStudentsByFirstName(final Collection students, final String firstName) { 220 | final ArrayList result = new ArrayList<>(students); 221 | result.removeIf(student -> !student.getFirstName().equals(firstName)); 222 | result.sort(STUDENT_COMPARATOR); 223 | return result; 224 | } 225 | 226 | @Override 227 | public List findStudentsByLastName(final Collection students, final String lastName) { 228 | final ArrayList result = new ArrayList<>(students); 229 | result.removeIf(student -> !student.getLastName().equals(lastName)); 230 | result.sort(STUDENT_COMPARATOR); 231 | return result; 232 | } 233 | 234 | @Override 235 | public List findStudentsByGroup(final Collection students, final GroupName group) { 236 | final ArrayList result = new ArrayList<>(students); 237 | result.removeIf(student -> !student.getGroup().equals(group)); 238 | result.sort(STUDENT_COMPARATOR); 239 | return result; 240 | } 241 | 242 | @Override 243 | public Map findStudentNamesByGroup(Collection students, GroupName group) { 244 | final Map result = new HashMap<>(); 245 | for (final Student student : findStudentsByGroup(students, group)) { 246 | result.merge(student.getLastName(), student.getFirstName(), BinaryOperator.minBy(Comparable::compareTo)); 247 | } 248 | return result; 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /OldWalkTest.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.ja.фамилия.walk; 2 | 3 | import info.kgeorgiy.java.advanced.base.BaseTest; 4 | import org.junit.Assert; 5 | import org.junit.FixMethodOrder; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.rules.TestName; 9 | import org.junit.runners.MethodSorters; 10 | 11 | import java.io.IOException; 12 | import java.io.PrintWriter; 13 | import java.io.StringWriter; 14 | import java.lang.reflect.InvocationTargetException; 15 | import java.lang.reflect.Method; 16 | import java.nio.charset.Charset; 17 | import java.nio.file.Files; 18 | import java.nio.file.InvalidPathException; 19 | import java.nio.file.Path; 20 | import java.util.*; 21 | import java.util.stream.Collectors; 22 | 23 | /** 24 | * Tests for easy version 25 | * of Walk homework 26 | * for Java Advanced course. 27 | * 28 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 29 | */ 30 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 31 | public class OldWalkTest extends BaseTest { 32 | protected static final Path DIR = Path.of("__Test__Walk__"); 33 | private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 34 | protected static final Random RANDOM = new Random(23084701432182342L); 35 | private String alphabet = ALPHABET; 36 | 37 | @Rule 38 | public TestName name = new TestName(); 39 | 40 | private Path getTestDir() { 41 | return DIR.resolve(name.getMethodName()); 42 | } 43 | 44 | @Test 45 | public void test10_oneEmptyFile() throws IOException { 46 | test(randomFiles(1, 0)); 47 | } 48 | 49 | @Test 50 | public void test15_tenEmptyFiles() throws IOException { 51 | test(randomFiles(10, 0)); 52 | } 53 | 54 | @Test 55 | public void test20_smallRandomFiles() throws IOException { 56 | test(randomFiles(10, 100)); 57 | } 58 | 59 | @Test 60 | public void test21_mediumRandomFiles() throws IOException { 61 | test(randomFiles(10, 100)); 62 | } 63 | 64 | @Test 65 | public void test22_largeRandomFiles() throws IOException { 66 | test(randomFiles(10, 1_000_000)); 67 | } 68 | 69 | @Test 70 | public void test23_veryLargeFile() throws IOException { 71 | test(randomFiles(1, 10_000_000)); 72 | } 73 | 74 | @Test 75 | public void test30_missingFiles() throws IOException { 76 | final Map files = randomFiles(3, 0); 77 | files.put(randomFileName(), 0L); 78 | files.put(randomFileName(), 0L); 79 | files.put(randomFileName(), 0L); 80 | test(files); 81 | } 82 | 83 | @Test 84 | public void test40_errorReading() throws IOException { 85 | final Map files = randomFiles(3, 0); 86 | files.put(DIR.toString() + "..", 0L); 87 | files.put(DIR.toString() + "@", 0L); 88 | test(files); 89 | } 90 | 91 | @Test 92 | public void test50_whitespaceSupport() throws IOException { 93 | testAlphabet(10, 100, " \u00a0_"); 94 | } 95 | 96 | @Test 97 | public void test55_chineseSupport() throws IOException { 98 | testAlphabet(10, 100, "\u8acb\u554f\u4f60\u7684\u7a0b\u5e8f\u652f\u6301\u4e2d\u570b"); 99 | } 100 | 101 | private void testAlphabet(final int n, final int maxL, final String alphabet) throws IOException { 102 | this.alphabet = alphabet; 103 | test(randomFiles(n, maxL)); 104 | this.alphabet = ALPHABET; 105 | } 106 | 107 | @Test 108 | public void test60_noInput() { 109 | runRaw(randomFileName(), randomFileName()); 110 | } 111 | 112 | @Test 113 | public void test61_invalidInput() { 114 | runRaw("/", randomFileName()); 115 | runRaw("\0*", randomFileName()); 116 | } 117 | 118 | @Test 119 | public void test62_invalidOutput() throws IOException { 120 | final String input = createEmptyFile(name.getMethodName()); 121 | runRaw(input, DIR.toString()); 122 | runRaw(input, "\0*"); 123 | final String file = createEmptyFile(name.getMethodName()); 124 | runRaw(input, file + "/" + randomFileName()); 125 | } 126 | 127 | @Test 128 | public void test63_invalidFiles() throws IOException { 129 | testAlphabet(1, 10, "\0\\*"); 130 | } 131 | 132 | @Test 133 | public void test70_singleArgument() throws IOException { 134 | runRaw(createEmptyFile(name.getMethodName())); 135 | } 136 | 137 | @Test 138 | public void test71_noArguments() { 139 | runRaw(); 140 | } 141 | 142 | @Test 143 | public void test72_nullArguments() { 144 | runRaw((String[]) null); 145 | } 146 | 147 | @Test 148 | public void test73_firstArgumentNull() { 149 | runRaw(null, ""); 150 | } 151 | 152 | @Test 153 | public void test74_secondArgumentNull() throws IOException { 154 | runRaw(createEmptyFile(name.getMethodName()), null); 155 | } 156 | 157 | @Test 158 | public void test75_threeArguments() throws IOException { 159 | runRaw(createEmptyFile("a"), createEmptyFile("b"), "c"); 160 | } 161 | 162 | @Test 163 | public void test80_partiallyMissingFiles() throws IOException { 164 | final Map files = new LinkedHashMap<>(); 165 | files.put("no-such-file-1", 0L); 166 | files.putAll(randomFiles(10, 100)); 167 | files.put("no-such-file-2", 0L); 168 | files.putAll(randomFiles(10, 100)); 169 | files.put("no-such-file-3", 0L); 170 | test(files); 171 | } 172 | 173 | private Map randomFiles(final int n, final int maxL) throws IOException { 174 | return randomFiles(n, maxL, getTestDir()); 175 | } 176 | 177 | private static String createEmptyFile(final String name) throws IOException { 178 | final Path input = DIR.resolve(name); 179 | Files.write(input, new byte[0]); 180 | return input.toString(); 181 | } 182 | 183 | protected void test(final Map files) { 184 | test(files.keySet(), files); 185 | } 186 | 187 | protected void test(final Collection inputs, final Map files) { 188 | final Path inputFile = DIR.resolve(name.getMethodName() + ".in"); 189 | final Path outputFile = DIR.resolve(name.getMethodName() + ".out"); 190 | try { 191 | Files.writeString(inputFile, generateInput(inputs)); 192 | } catch (final IOException e) { 193 | throw new AssertionError("Cannot write input file " + inputFile); 194 | } 195 | run(inputFile, outputFile); 196 | try { 197 | for (final String line : Files.readAllLines(outputFile, Charset.forName("UTF-8"))) { 198 | final String[] parts = line.split(" ", 2); 199 | Assert.assertEquals("Invalid line format\n" + line, 2, parts.length); 200 | Assert.assertTrue("Unexpected file " + parts[1], files.containsKey(parts[1])); 201 | Assert.assertEquals("Wrong hash", String.format("%016x", files.remove(parts[1])), parts[0]); 202 | } 203 | } catch (final IOException e) { 204 | throw new AssertionError("Cannot write output file " + outputFile); 205 | } 206 | 207 | Assert.assertTrue("Some files missing: \n " + String.join("\n ", files.keySet()), files.isEmpty()); 208 | } 209 | 210 | private void run(final Path inputFile, final Path outputFile) { 211 | runRaw(inputFile.toString(), outputFile.toString()); 212 | } 213 | 214 | private void runRaw(final String... args) { 215 | final Method method; 216 | final Class cut = loadClass(); 217 | try { 218 | method = cut.getMethod("main", String[].class); 219 | } catch (final NoSuchMethodException e) { 220 | throw new AssertionError("Cannot find method main(String[]) of " + cut, e); 221 | } 222 | System.out.println("Running " + name.getMethodName()); 223 | try { 224 | method.invoke(null, (Object) args); 225 | } catch (final IllegalAccessException e) { 226 | throw new AssertionError("Cannot call main(String[]) of " + cut, e); 227 | } catch (final InvocationTargetException e) { 228 | throw new AssertionError("Error thrown", e.getCause()); 229 | } 230 | } 231 | 232 | private static String generateInput(final Collection files) { 233 | final StringWriter stringWriter = new StringWriter(); 234 | final PrintWriter writer = new PrintWriter(stringWriter); 235 | files.forEach(writer::println); 236 | writer.close(); 237 | return stringWriter.toString(); 238 | } 239 | 240 | protected Map randomFiles(final int n, final int maxL, final Path dir) throws IOException { 241 | Files.createDirectories(dir); 242 | final Map result = new HashMap<>(); 243 | for (int i = 0; i < n; i++) { 244 | final String name = randomFileName(); 245 | try { 246 | final Path file = dir.resolve(name); 247 | final byte[] bytes = new byte[RANDOM.nextInt(maxL + 1)]; 248 | RANDOM.nextBytes(bytes); 249 | Files.write(file, bytes); 250 | result.put(file.toString(), hash(bytes)); 251 | } catch (final InvalidPathException ignore) { 252 | result.put(dir + "/" + name, 0L); 253 | } 254 | } 255 | return result; 256 | } 257 | 258 | protected String randomFileName() { 259 | return RANDOM.ints(30, 0, alphabet.length()) 260 | .mapToObj(i -> alphabet.substring(i, i + 1)) 261 | .collect(Collectors.joining()); 262 | } 263 | 264 | public static long hash(final byte[] bytes) { 265 | long start = 0; 266 | for (final byte b : bytes) { 267 | start = (start << 8) + (b & 0xff); 268 | final long high = start & 0xff00_0000_0000_0000L; 269 | if (high != 0) { 270 | start ^= high >> 48; 271 | start &= ~high; 272 | } 273 | } 274 | return start; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java-advanced-hw-testing 2 | Инструкции по настройке тестов (в том числе кастомных) для домашек по Java Advanced. 3 | 4 | Также в репозитории находятся новые старые тесты, на которых может быть очень полезно погонять ваше решение. 5 | 6 | ### Шаг 1 7 | Склонируйте реп и откройте его в IDEA. 8 | ```shell script 9 | > git clone https://www.kgeorgiy.info/git/geo/java-advanced-2021 10 | ``` 11 | 12 | Все должно светится красным. 13 | 14 | ### Шаг 2 15 | Открываем `File -> Project Structure -> Modules`, видим следующую картину: 16 | ![](img/1.png) 17 | 18 | Теперь нам надо добавить в сурсы модулям **base** и **walk** либы, которые у нас лежат в виде джарников. 19 | Делается это так - нажимаем на модуль, нажимаем на `+`, выбираем `library`, открывается такое меню: 20 | ![](img/2.png) 21 | 22 | Здесь надо выбрать папку `lib`. Повторяем это для обоих модулей. 23 | 24 | #### Это важный чекпоинт - сейчас у вас не должно быть красных подчеркиваний в Гошиных файлах. Если это не так, перезапустите IDEA. 25 | 26 | ### Шаг 3 27 | 28 | Создаем свой модуль. Для этого во всё том же `Project Structure` нажимаем `+` над списком модулей, 29 | следуем по инструкции, кладем его в пакет к двум другим модулям. 30 | 31 | ![](img/3.png) 32 | 33 | Теперь аккуратно, следите за руками. Надо открыть наш модуль, в нём вкладку `Sources` и удалить оттуда 34 | папку `src` (физически папка не удалится). 35 | 36 | После этого надо добавить туда корень нашего модуля (он отображается как `.`). 37 | 38 | ![](img/4.png) 39 | ![](img/6.png) 40 | 41 | Далее, надо добавить нашему модулю в сурсы либы и Гошины модули. Либы мы уже добавляли, модули добавляются так же. 42 | 43 | ![](img/5.png) 44 | 45 | Теперь в модуле нужно создать [module-info](module-info.java) именно с таким контентом. 46 | Затем создаём в нашем модуле пакет с названием как у модуля и внутри - наши файлы с решением. 47 | Это почти все. 48 | 49 | ![](img/7.png) 50 | 51 | ### Шаг 4 - кастомные тесты 52 | 53 | В нашем пакете создаем [CustomTester](CustomTester.java). Он экстендит Гошин оригинальный тестер без 54 | изменений кроме `psvm` потому, что мы не хотим менять оригинальные тесты. 55 | 56 | Кроме этого, создаем там же [CustomTest](CustomTest.java). Это файл, где мы будем хранить кастомные тесты. 57 | 58 | *Ремарка: можно не экстендить `RecursiveWalkTest`, тогда при запуске конфигурации будут гоняться 59 | __только__ кастомные тесты.* 60 | 61 | Теперь идем в конфигурации, нажимаем `Add Configuration... -> + -> Application`, конфиг делаем 62 | в точности такой (имя можно менять): 63 | 64 | ![](img/8.png) 65 | 66 | Все. Теперь при запуске этой конфигурации у вас будут гонять все тесты. Если хотите запустить только 67 | Гошины, то в строке `Program arguments` замените `Custom` на те тесты, которые вам нужны 68 | (вероятно, `Recursive`). 69 | 70 | ### Шаг 5 - как удобно решать таски 71 | 72 | Авторский совет — решайте таски в своём клоне этого репозитория (где тесты), а когда решили — 73 | пушьте в личный репозиторий. Дальше гайд от меня, как сделать ваш опыт сдачи чуть приятнее и удобнее. 74 | 75 | Когда клонируете свой репозиторий, используйте команду 76 | ```shell 77 | > git clone http://ФАМИЛИЯ_ИМЯ:ПАРОЛЬ@www.kgeorgiy.info/git-students/year2019/ФАМИЛИЯ_ИМЯ/java-advanced 78 | ``` 79 | 80 | Если же вы уже клонировали его, то можно использовать команду 81 | 82 | ```shell 83 | > git remote set-url origin http://ФАМИЛИЯ_ИМЯ:ПАРОЛЬ@www.kgeorgiy.info/git-students/year2019/ФАМИЛИЯ_ИМЯ/java-advanced 84 | ``` 85 | 86 | **КОММЕНТАРИЙ ОТ КГЕОРГИЯ:** 87 | Пожалуйста, не надо так делать! 88 | 1. Вы клонируете репозиторий по http (а не https) 89 | 2. Для этого есть [специальный механизм](https://git-scm.com/docs/git-credential-store), который работает всегда, а не только с некоторым схемами URL. 90 | 91 | Как можно понять, вы просто сразу задаете гиту свои логин и пароль, запоминая их на будущее. Теперь вам 92 | не придется вводить их каждый раз когда вы пушите. Это же поможет нам использовать 93 | [bash-скрипт](submit.sh). 94 | 95 | Этот скрипт сделает рекурсивную копию вашего модуля, игнорируя файлы IDEA и `module-info.java`, 96 | а также все `.java` файлы, имеющие суффикс `Test` или `Tester`. 97 | 98 | Помните, что скрипт может насрать, если вы предварительно не запуллили Гошины правки/замечания 99 | с помощью другого [замечательного скрипта](refre.sh). 100 | 101 | ### Шаг 6 - а ведь есть и другие задания 102 | Да, есть. Процесс точно обобщается и для них. Каждый раз, когда выкладываются тесты, не забывайте 103 | пуллить их к себе в этот репозиторий, и жизнь ваша будет хороша. 104 | -------------------------------------------------------------------------------- /SortedSetTest.java: -------------------------------------------------------------------------------- 1 | package info.kgeorgiy.ja.фамилия.arrayset; 2 | 3 | import info.kgeorgiy.java.advanced.base.BaseTest; 4 | import net.java.quickcheck.Generator; 5 | import net.java.quickcheck.collection.Pair; 6 | import org.junit.Assert; 7 | import org.junit.FixMethodOrder; 8 | import org.junit.Test; 9 | import org.junit.runners.MethodSorters; 10 | 11 | import java.util.*; 12 | import java.util.stream.Collectors; 13 | 14 | import static net.java.quickcheck.generator.CombinedGenerators.excludeValues; 15 | import static net.java.quickcheck.generator.CombinedGenerators.lists; 16 | import static net.java.quickcheck.generator.CombinedGeneratorsIterables.*; 17 | import static net.java.quickcheck.generator.PrimitiveGenerators.fixedValues; 18 | import static net.java.quickcheck.generator.PrimitiveGenerators.integers; 19 | 20 | /** 21 | * Tests for easy version 22 | * of ArraySet homework 23 | * for Java Advanced course. 24 | * 25 | * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) 26 | */ 27 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 28 | public class SortedSetTest extends BaseTest { 29 | public static final int PERFORMANCE_SIZE = 1_000_000; 30 | public static final int PERFORMANCE_TIME = 10_000; 31 | 32 | @Test 33 | public void test01_constructors() { 34 | final Class token = loadClass(); 35 | Assert.assertTrue(token.getName() + " should implement SortedSet interface", SortedSet.class.isAssignableFrom(token)); 36 | 37 | checkConstructor("default constructor", token); 38 | checkConstructor("constructor out of Collection", token, Collection.class); 39 | checkConstructor("constructor out of Collection and Comparator", token, Collection.class, Comparator.class); 40 | } 41 | 42 | @Test 43 | public void test02_empty() { 44 | final SortedSet set = create(new Object[]{}); 45 | Assert.assertEquals("Empty set size should be zero", 0, set.size()); 46 | Assert.assertTrue("Empty set should be empty", set.isEmpty()); 47 | Assert.assertEquals("toArray for empty set should return empty array", 0, (Object) set.toArray().length); 48 | } 49 | 50 | @Test 51 | public void test03_naturalOrder() { 52 | for (final List elements : someLists(integers())) { 53 | final SortedSet set = set(elements); 54 | final SortedSet treeSet = treeSet(elements); 55 | assertEq(set, treeSet, "elements = " + elements); 56 | } 57 | } 58 | 59 | @Test 60 | public void test04_externalOrder() { 61 | for (final Pair> pair : withComparator()) { 62 | final List elements = pair.getSecond(); 63 | final Comparator comparator = pair.getFirst(); 64 | 65 | assertEq( 66 | set(elements, comparator), 67 | treeSet(elements, comparator), 68 | "(comparator = " + comparator + ", elements = " + elements + ")" 69 | ); 70 | } 71 | } 72 | 73 | protected static Iterable>> withComparator() { 74 | return somePairs(NAMED_COMPARATORS, lists(integers())); 75 | } 76 | 77 | @Test 78 | public void test05_constructorPerformance() { 79 | performance("constructor", () -> performanceSet(PERFORMANCE_SIZE)); 80 | } 81 | 82 | @Test 83 | public void test06_immutable() { 84 | final SortedSet set = set(Collections.singletonList(1)); 85 | checkUnsupported("add", () -> set.add(1)); 86 | checkUnsupported("addAll", () -> set.addAll(Collections.singletonList(1))); 87 | checkUnsupported("clear", set::clear); 88 | checkUnsupported("iterator.remove", () -> { 89 | final Iterator iterator = set.iterator(); 90 | iterator.next(); 91 | iterator.remove(); 92 | }); 93 | checkUnsupported("remove", () -> set.remove(1)); 94 | checkUnsupported("removeAll", () -> set.removeAll(Collections.singletonList(1))); 95 | checkUnsupported("retainAll", () -> set.retainAll(Collections.singletonList(0))); 96 | } 97 | 98 | private static void checkUnsupported(final String method, final Runnable command) { 99 | try { 100 | command.run(); 101 | Assert.fail("Method '" + method + "' should throw UnsupportedOperationException"); 102 | } catch (final UnsupportedOperationException ignore) { 103 | } 104 | } 105 | 106 | @Test 107 | public void test07_contains() { 108 | for (final Pair> pair : withComparator()) { 109 | final List elements = pair.getSecond(); 110 | final Comparator comparator = pair.getFirst(); 111 | 112 | final SortedSet set = set(elements, comparator); 113 | final String context = "(comparator = " + comparator + ", elements = " + elements + ")"; 114 | for (final Integer element : elements) { 115 | Assert.assertTrue("set should contains() element " + element + " " + context, set.contains(element)); 116 | } 117 | 118 | final SortedSet treeSet = treeSet(elements, comparator); 119 | for (final Integer element : someOneOf(excludeValues(integers(), elements))) { 120 | Assert.assertEquals("contains(" + element + ") " + context, treeSet.contains(element), set.contains(element)); 121 | } 122 | } 123 | } 124 | 125 | @Test 126 | public void test08_containsPerformance() { 127 | performance("contains", () -> { 128 | final SortedSet set = performanceSet(PERFORMANCE_SIZE); 129 | for (final Integer element : set) { 130 | Assert.assertTrue(null, set.contains(element)); 131 | } 132 | }); 133 | } 134 | 135 | @Test 136 | public void test09_containsAll() { 137 | for (final Pair> pair : withComparator()) { 138 | final List elements = pair.getSecond(); 139 | final Comparator comparator = pair.getFirst(); 140 | 141 | final SortedSet set = set(elements, comparator); 142 | final String context = "(comparator = " + comparator + ", elements = " + elements + ")"; 143 | Assert.assertTrue("set should contains() all elements " + " " + context, set.containsAll(elements)); 144 | 145 | final SortedSet treeSet = treeSet(elements, comparator); 146 | for (final Integer element : someOneOf(excludeValues(integers(), elements))) { 147 | final List l = new ArrayList<>(elements); 148 | elements.add(element); 149 | Assert.assertEquals("containsAll(" + l + ") " + context, treeSet.containsAll(l), set.containsAll(l)); 150 | } 151 | } 152 | } 153 | 154 | @Test 155 | public void test10_containsAllPerformance() { 156 | performance("contains", () -> { 157 | final SortedSet set = performanceSet(PERFORMANCE_SIZE); 158 | Assert.assertTrue(null, set.containsAll(new ArrayList<>(set))); 159 | }); 160 | } 161 | 162 | private static void performance(final String description, final Runnable runnable) { 163 | runnable.run(); 164 | 165 | final long start = System.currentTimeMillis(); 166 | runnable.run(); 167 | final long time = System.currentTimeMillis() - start; 168 | System.out.println(" " + description + " done in " + time + "ms"); 169 | Assert.assertTrue(description + " works too slow", time < PERFORMANCE_TIME); 170 | } 171 | 172 | private SortedSet performanceSet(final int size) { 173 | return set(new Random().ints().limit(size).boxed().collect(Collectors.toList())); 174 | } 175 | 176 | private static List toList(final SortedSet set) { 177 | return new ArrayList<>(set); 178 | } 179 | 180 | protected static List toArray(final SortedSet set) { 181 | return Arrays.asList(set.toArray(new Number[0])); 182 | } 183 | 184 | private static TreeSet treeSet(final List elements) { 185 | return new TreeSet<>(elements); 186 | } 187 | 188 | private SortedSet set(final List elements) { 189 | return create(new Object[]{elements}, Collection.class); 190 | } 191 | 192 | protected SortedSet set(final List elements, final Comparator comparator) { 193 | return create(new Object[]{elements, comparator}, Collection.class, Comparator.class); 194 | } 195 | 196 | protected static void assertEq(final SortedSet set, final SortedSet treeSet, final String context) { 197 | Assert.assertEquals("invalid element order " + context, toList(treeSet), toList(set)); 198 | Assert.assertEquals("invalid toArray " + context, toArray(set), toArray(set)); 199 | Assert.assertEquals("invalid set size " + context, treeSet.size(), (Object) set.size()); 200 | Assert.assertEquals("invalid isEmpty " + context, treeSet.isEmpty(), set.isEmpty()); 201 | Assert.assertSame("invalid comparator " + context, treeSet.comparator(), set.comparator()); 202 | } 203 | 204 | protected SortedSet treeSet(final List elements, final Comparator comparator) { 205 | final SortedSet set = new TreeSet<>(comparator); 206 | set.addAll(elements); 207 | return set; 208 | } 209 | 210 | protected static final class NamedComparator implements Comparator { 211 | private final String name; 212 | private final Comparator comparator; 213 | 214 | private NamedComparator(final String name, final Comparator comparator) { 215 | this.name = name; 216 | this.comparator = comparator != null ? comparator : Comparator.naturalOrder(); 217 | } 218 | 219 | @Override 220 | public int compare(final Integer o1, final Integer o2) { 221 | return comparator.compare(o1, o2); 222 | } 223 | 224 | @Override 225 | public String toString() { 226 | return name; 227 | } 228 | } 229 | 230 | private static final Generator NAMED_COMPARATORS = fixedValues(Arrays.asList( 231 | new NamedComparator("Natural order", Integer::compare), 232 | new NamedComparator("Reverse order", Comparator.comparingInt(Integer::intValue).reversed()), 233 | new NamedComparator("Div 100", Comparator.comparingInt(i -> i / 100)), 234 | new NamedComparator("Even first", Comparator.comparingInt(i -> i % 2).thenComparing(Integer::intValue)), 235 | new NamedComparator("All equal", Comparator.comparingInt(i -> 0)), 236 | null 237 | )); 238 | 239 | private SortedSet create(final Object[] params, final Class... types) { 240 | try { 241 | @SuppressWarnings("unchecked") final 242 | SortedSet set = (SortedSet) loadClass().getConstructor(types).newInstance(params); 243 | return set; 244 | } catch (final Exception e) { 245 | e.printStackTrace(); 246 | Assert.fail("Instantiation error"); 247 | throw new AssertionError(); 248 | } 249 | } 250 | 251 | @Test 252 | public void test11_comparator() { 253 | for (final Pair> pair : withComparator()) { 254 | final List elements = pair.getSecond(); 255 | final Comparator comparator = pair.getFirst(); 256 | 257 | Assert.assertSame("comparator() should return provided comparator", comparator, set(elements, comparator).comparator()); 258 | } 259 | for (final List elements : someLists(integers())) { 260 | Assert.assertNull("comparator() should return null for default order", set(elements).comparator()); 261 | } 262 | } 263 | 264 | @Test 265 | public void test12_headSet() { 266 | for (final Pair> pair : withComparator()) { 267 | final List elements = pair.getSecond(); 268 | final Comparator comparator = pair.getFirst(); 269 | final SortedSet set = set(elements, comparator); 270 | final SortedSet treeSet = treeSet(elements, comparator); 271 | 272 | for (final Integer element : inAndOut(elements)) { 273 | assertEq( 274 | set.headSet(element), 275 | treeSet.headSet(element), 276 | "in headSet(" + element + ") (comparator = " + comparator + ", elements = " + elements + ")" 277 | ); 278 | } 279 | } 280 | } 281 | 282 | @Test 283 | public void test13_tailSet() { 284 | for (final Pair> pair : withComparator()) { 285 | final List elements = pair.getSecond(); 286 | final Comparator comparator = pair.getFirst(); 287 | final SortedSet set = set(elements, comparator); 288 | final SortedSet treeSet = treeSet(elements, comparator); 289 | 290 | for (final Integer element : inAndOut(elements)) { 291 | assertEq( 292 | set.tailSet(element), 293 | treeSet.tailSet(element), 294 | "in tailSet(" + element + ") (comparator = " + comparator + ", elements = " + elements + ")" 295 | ); 296 | } 297 | } 298 | } 299 | 300 | protected Collection inAndOut(final List elements) { 301 | return concat(elements, someOneOf(excludeValues(integers(), elements))); 302 | } 303 | 304 | private Collection concat(final Iterable items1, final Iterable items2) { 305 | final List list = new ArrayList<>(); 306 | items1.forEach(list::add); 307 | items2.forEach(list::add); 308 | return list; 309 | } 310 | 311 | @Test 312 | public void test14_subSet() { 313 | for (final Pair> pair : withComparator()) { 314 | final List elements = pair.getSecond(); 315 | final Comparator comparator = pair.getFirst(); 316 | final SortedSet set = set(elements, comparator); 317 | final SortedSet treeSet = treeSet(elements, comparator); 318 | 319 | final Collection all = values(elements); 320 | for (final Pair p : somePairs(fixedValues(all), fixedValues(all))) { 321 | final Integer from = p.getFirst(); 322 | final Integer to = p.getSecond(); 323 | if (comparator == null ? from <= to : comparator.compare(from, to) <= 0) { 324 | assertEq( 325 | set.subSet(from, to), 326 | treeSet.subSet(from, to), 327 | "in subSet(" + from + ", " + to + ") (comparator = " + comparator + ", elements = " + elements + ")" 328 | ); 329 | } else { 330 | try { 331 | set.subSet(from, to); 332 | Assert.fail("IllegalArgumentException expected"); 333 | } catch (final IllegalArgumentException ignored) { 334 | // Passed 335 | } 336 | } 337 | } 338 | } 339 | } 340 | 341 | protected Collection values(final List elements) { 342 | return concat(inAndOut(elements), Arrays.asList(0, Integer.MAX_VALUE, Integer.MIN_VALUE)); 343 | } 344 | 345 | @Test 346 | public void test15_tailSetPerformance() { 347 | performance("tailSet", () -> { 348 | final SortedSet set = performanceSet(PERFORMANCE_SIZE); 349 | for (final Integer element : set) { 350 | Assert.assertTrue(null, set.tailSet(element).contains(element)); 351 | } 352 | }); 353 | } 354 | 355 | @Test 356 | public void test16_first() { 357 | for (final Pair> pair : withComparator()) { 358 | final List elements = pair.getSecond(); 359 | final Comparator comparator = pair.getFirst(); 360 | 361 | final SortedSet set = set(elements, comparator); 362 | if (set.isEmpty()) { 363 | try { 364 | set.first(); 365 | Assert.fail("first() should throw NoSuchElementException for empty set"); 366 | } catch (final NoSuchElementException e) { 367 | // Expected behavior 368 | } 369 | } else { 370 | Assert.assertEquals("first() " + "(comparator = " + comparator + ", elements = " + elements + ")", set(elements, comparator).first(), set.first()); 371 | } 372 | } 373 | } 374 | 375 | @Test 376 | public void test17_last() { 377 | for (final Pair> pair : withComparator()) { 378 | final List elements = pair.getSecond(); 379 | final Comparator comparator = pair.getFirst(); 380 | 381 | final SortedSet set = set(elements, comparator); 382 | if (set.isEmpty()) { 383 | try { 384 | set.last(); 385 | Assert.fail("last() should throw NoSuchElementException for empty set"); 386 | } catch (final NoSuchElementException e) { 387 | // ok 388 | } 389 | } else { 390 | Assert.assertEquals("last() " + "(comparator = " + comparator + ", elements = " + elements + ")", set(elements, comparator).last(), set.last()); 391 | } 392 | } 393 | } 394 | 395 | @Test 396 | public void test18_copySource() { 397 | final List list = new ArrayList<>(List.of(1, 10, 100)); 398 | final SortedSet integers = create(new Object[]{list}, Collection.class); 399 | assertEq(new TreeSet<>(List.of(1, 10, 100)), integers, "initial"); 400 | list.set(1, 20); 401 | assertEq(new TreeSet<>(List.of(1, 10, 100)), integers, "mutated"); 402 | } 403 | 404 | @Test 405 | public void test19_immutableSource() { 406 | final SortedSet integers = create(new Object[]{List.of(1, 100, 10)}, Collection.class); 407 | assertEq(new TreeSet<>(List.of(1, 10, 100)), integers, "initial"); 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Covariance/java-advanced-hw-testing/89ea61f199d31bf17ad7111b5b8aa01397568465/img/1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Covariance/java-advanced-hw-testing/89ea61f199d31bf17ad7111b5b8aa01397568465/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Covariance/java-advanced-hw-testing/89ea61f199d31bf17ad7111b5b8aa01397568465/img/3.png -------------------------------------------------------------------------------- /img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Covariance/java-advanced-hw-testing/89ea61f199d31bf17ad7111b5b8aa01397568465/img/4.png -------------------------------------------------------------------------------- /img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Covariance/java-advanced-hw-testing/89ea61f199d31bf17ad7111b5b8aa01397568465/img/5.png -------------------------------------------------------------------------------- /img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Covariance/java-advanced-hw-testing/89ea61f199d31bf17ad7111b5b8aa01397568465/img/6.png -------------------------------------------------------------------------------- /img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Covariance/java-advanced-hw-testing/89ea61f199d31bf17ad7111b5b8aa01397568465/img/7.png -------------------------------------------------------------------------------- /img/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Covariance/java-advanced-hw-testing/89ea61f199d31bf17ad7111b5b8aa01397568465/img/8.png -------------------------------------------------------------------------------- /module-info.java: -------------------------------------------------------------------------------- 1 | module info.kgeorgiy.ja.фамилия.walk { 2 | requires transitive info.kgeorgiy.java.advanced.walk; 3 | 4 | exports info.kgeorgiy.ja.фамилия.walk; 5 | 6 | opens info.kgeorgiy.ja.фамилия.walk to junit; 7 | } -------------------------------------------------------------------------------- /refre.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pushd ../java-advanced || exit 4 | 5 | git fetch origin 6 | git fetch source 7 | git pull origin master 8 | git pull source master 9 | 10 | popd || exit -------------------------------------------------------------------------------- /submit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | (( $# != 1 )) \ 4 | && echo "Usage: $0 " \ 5 | && exit 6 | 7 | rsync -av --exclude='*Test.java' --exclude='*Tester.java' \ 8 | --exclude='*.iml' --exclude='*module-info.java' \ 9 | ./modules/info.kgeorgiy.ja.фамилия."$1"/ \ 10 | ../java-advanced/java-solutions 11 | 12 | pushd ../java-advanced || exit 13 | 14 | git add ./java-solutions/info/kgeorgiy/ja/фамилия/"$1"/* 15 | git commit -m "Add $1 [Automated commit]" 16 | git push origin master 17 | 18 | popd || exit --------------------------------------------------------------------------------