Skip to content
Snippets Groups Projects
Commit e59fe78b authored by Torstein Strømme's avatar Torstein Strømme
Browse files

First draft

parents
No related branches found
No related tags found
No related merge requests found
File added
src/main/logo/windows/logo.ico

61.1 KiB

src/main/resources/no/uib/inf101/gridview/app-icon400.png

144 KiB

package no.uib.inf101.colorgrid;
import org.junit.jupiter.api.Test;
import java.awt.Color;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import static org.junit.jupiter.api.Assertions.*;
public class TestColorGrid {
@Test
public void testSize() {
IColorGrid grid = createGrid(10, 20);
assertEquals(10, grid.rows());
assertEquals(20, grid.cols());
grid = createGrid(1, 1);
assertEquals(1, grid.rows());
assertEquals(1, grid.cols());
}
@Test
public void testGetDefaultValue() {
IColorGrid grid = createGrid(10, 20);
assertNull(grid.get(new CellPosition(0, 0)));
assertNull(grid.get(new CellPosition(2, 3)));
}
@Test
public void testSetGetInCorners() {
IColorGrid grid = createGrid(10, 20);
// Set color in corners
grid.set(new CellPosition(0, 0), Color.RED);
grid.set(new CellPosition(0, 19), Color.GREEN);
grid.set(new CellPosition(9, 0), Color.BLUE);
grid.set(new CellPosition(9, 19), Color.YELLOW);
// Get color in corners
assertEquals(Color.RED, grid.get(new CellPosition(0, 0)));
assertEquals(Color.GREEN, grid.get(new CellPosition(0, 19)));
assertEquals(Color.BLUE, grid.get(new CellPosition(9, 0)));
assertEquals(Color.YELLOW, grid.get(new CellPosition(9, 19)));
}
@Test
public void testIndexOutOfBoundsException() {
IColorGrid grid = createGrid(10, 20);
// Test out of bounds for get
assertThrows(IndexOutOfBoundsException.class, () -> grid.get(new CellPosition(-1, 0)));
assertThrows(IndexOutOfBoundsException.class, () -> grid.get(new CellPosition(0, -1)));
assertThrows(IndexOutOfBoundsException.class, () -> grid.get(new CellPosition(10, 0)));
assertThrows(IndexOutOfBoundsException.class, () -> grid.get(new CellPosition(0, 20)));
// Test out of bounds for set
assertThrows(IndexOutOfBoundsException.class, () -> grid.set(new CellPosition(-1, 0), Color.RED));
assertThrows(IndexOutOfBoundsException.class, () -> grid.set(new CellPosition(0, -1), Color.RED));
assertThrows(IndexOutOfBoundsException.class, () -> grid.set(new CellPosition(10, 0), Color.RED));
assertThrows(IndexOutOfBoundsException.class, () -> grid.set(new CellPosition(0, 20), Color.RED));
}
/**
* Create a new ColorGrid with the given rows and cols. This method
* will only work if you have implemented the ColorGrid class with
* the correct parameters (two int's), otherwise the test will fail
* when calling this method.
*
* @param rows number of rows in the colorgrid to create
* @param cols number of columns in the colorgrid to create
* @return a new ColorGrid
*/
public IColorGrid createGrid(int rows, int cols) {
try {
Constructor<?> c = ColorGrid.class.getConstructor(int.class, int.class);
Object o = c.newInstance(rows, cols);
if (o instanceof IColorGrid grid) {
return grid;
}
fail("Constructor did not return an IColorGrid. This could be "
+ "because you forgot to implement the IColorGrid interface.");
} catch (NoSuchMethodException e) {
fail("Could not find constructor ColorGrid(int, int)");
} catch (InvocationTargetException e) {
fail("Constructor crashed: " + e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
fail("Constructor is not public: " + e);
}
return null;
}
}
package no.uib.inf101.colorgrid;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestTextQuestions {
@Test
public void q1() {
assertEquals(5, TextQuestions.a1,
"A class implementing IColorGrid must implement all " +
"methods from IColorGrid, including methods inherited from " +
"GridDimension and CellColorCollection"
);
}
@Test
public void q2() {
assertEquals(true, TextQuestions.a2,
"If an interface A extends interface B, then any " +
"class implementing A must also implement " +
"B, and objects in such a class will " +
"have the type of both interfaces."
);
}
@Test
public void q3() {
assertEquals(false, TextQuestions.a3,
"It is possible to create a class which implements" +
"CellColorCollection directly, without implementing IColorGrid. " +
"Objects in such a class would have the type GridDimension, " +
"but not the type IColorGrid."
);
}
@Test
public void q4() {
assertEquals(false, TextQuestions.a4,
"It is possible to create a class which implements" +
"GridDimension directly, without implementing IColorGrid or" +
"CellColorCollection. Objects in such a class would have the " +
"type GridDimension, but not the type CellColorCollection."
);
}
@Test
public void q5() {
assertEquals(true, TextQuestions.a5,
"If an object has the type IColorGrid, it must indeed" +
"belong to a class that implements the interface IColorGrid." +
"If the class does not do so directly, it must do so" +
"indirectly through inheritance."
);
}
@Test
public void q6() {
assertEquals(true, TextQuestions.a6,
"If a class implements IColorGrid, then it will also " +
"implement GridDimension, since IColorGrid extends it. Any " +
"object in such a class will have the types IColorGrid, " +
"GridDimension, and CellColorCollection."
);
}
}
package no.uib.inf101.gridview;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ImageObserver;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.RenderableImage;
import java.text.AttributedCharacterIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* A dummy Graphics2D that records all shapes and colors that are filled
* instead of actually drawing anything.
*/
public class RecordGraphics2D extends java.awt.Graphics2D {
private Color color = null;
private final List<Shape> fillRecordShapes = new ArrayList<>();
private final List<Color> fillRecordColors = new ArrayList<>();
public List<Shape> getFillRecordShapes() {
return this.fillRecordShapes;
}
public List<Color> getFillRecordColors() {
return this.fillRecordColors;
}
public void resetRecord() {
this.fillRecordShapes.clear();
this.fillRecordColors.clear();
}
@Override
public void draw(Shape s) {
// dummy ignores
}
@Override
public void fill(Shape s) {
this.fillRecordShapes.add(s);
this.fillRecordColors.add(this.color);
}
@Override
public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
return false;
}
@Override
public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {
// dummy ignores
}
@Override
public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
// dummy ignores
}
@Override
public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
// dummy ignores
}
@Override
public void drawString(String str, int x, int y) {
// dummy ignores
}
@Override
public void drawString(String str, float x, float y) {
// dummy ignores
}
@Override
public void drawString(AttributedCharacterIterator iterator, int x, int y) {
// dummy ignores
}
@Override
public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void dispose() {
// dummy ignores
}
@Override
public void drawString(AttributedCharacterIterator iterator, float x, float y) {
// dummy ignores
}
@Override
public void drawGlyphVector(GlyphVector g, float x, float y) {
// dummy ignores
}
@Override
public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void setComposite(Composite comp) {
// dummy ignores
}
@Override
public void setPaint(Paint paint) {
// dummy ignores
}
@Override
public void setStroke(Stroke s) {
// dummy ignores
}
@Override
public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) {
// dummy ignores
}
@Override
public Object getRenderingHint(RenderingHints.Key hintKey) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void setRenderingHints(Map<?, ?> hints) {
// dummy ignores
}
@Override
public void addRenderingHints(Map<?, ?> hints) {
// dummy ignores
}
@Override
public RenderingHints getRenderingHints() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Graphics create() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void translate(int x, int y) {
// dummy ignores
}
@Override
public Color getColor() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void setColor(Color c) {
this.color = c;
}
@Override
public void setPaintMode() {
// dummy ignores
}
@Override
public void setXORMode(Color c1) {
// dummy ignores
}
@Override
public Font getFont() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void setFont(Font font) {
// dummy ignores
}
@Override
public FontMetrics getFontMetrics(Font f) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Rectangle getClipBounds() {
return null;
}
@Override
public void clipRect(int x, int y, int width, int height) {
// dummy ignores
}
@Override
public void setClip(int x, int y, int width, int height) {
// dummy ignores
}
@Override
public Shape getClip() {
return null;
}
@Override
public void setClip(Shape clip) {
// dummy ignores
}
@Override
public void copyArea(int x, int y, int width, int height, int dx, int dy) {
// dummy ignores
}
@Override
public void drawLine(int x1, int y1, int x2, int y2) {
Shape shape = new Line2D.Double(x1, y1, x2, y2);
this.draw(shape);
}
@Override
public void fillRect(int x, int y, int width, int height) {
Shape shape = new Rectangle(x, y, width, height);
this.fill(shape);
}
@Override
public void clearRect(int x, int y, int width, int height) {
}
@Override
public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
Shape shape = new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight);
this.draw(shape);
}
@Override
public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
Shape shape = new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight);
this.fill(shape);
}
@Override
public void drawOval(int x, int y, int width, int height) {
Shape shape = new Ellipse2D.Double(x, y, width, height);
this.draw(shape);
}
@Override
public void fillOval(int x, int y, int width, int height) {
Shape shape = new Ellipse2D.Double(x, y, width, height);
this.fill(shape);
}
@Override
public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
Shape shape = new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN);
this.draw(shape);
}
@Override
public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
Shape shape = new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.PIE);
this.fill(shape);
}
@Override
public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
Path2D shape = new Path2D.Double();
shape.moveTo(xPoints[0], yPoints[0]);
for (int i = 1; i < nPoints; i++) {
shape.lineTo(xPoints[i], yPoints[i]);
}
this.draw(shape);
}
@Override
public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
Shape shape = new Polygon(xPoints, yPoints, nPoints);
this.draw(shape);
}
@Override
public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
Shape shape = new Polygon(xPoints, yPoints, nPoints);
this.fill(shape);
}
@Override
public void translate(double tx, double ty) {
// dummy ignores
}
@Override
public void rotate(double theta) {
// dummy ignores
}
@Override
public void rotate(double theta, double x, double y) {
// dummy ignores
}
@Override
public void scale(double sx, double sy) {
// dummy ignores
}
@Override
public void shear(double shx, double shy) {
// dummy ignores
}
@Override
public void transform(AffineTransform Tx) {
// dummy ignores
}
@Override
public void setTransform(AffineTransform Tx) {
// dummy ignores
}
@Override
public AffineTransform getTransform() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Paint getPaint() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Composite getComposite() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void setBackground(Color color) {
// dummy ignores
}
@Override
public Color getBackground() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Stroke getStroke() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void clip(Shape s) {
// dummy ignores
}
@Override
public FontRenderContext getFontRenderContext() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
package no.uib.inf101.gridview;
import no.uib.inf101.colorgrid.CellColor;
import no.uib.inf101.colorgrid.CellPosition;
import no.uib.inf101.colorgrid.GridDimension;
import no.uib.inf101.colorgrid.IColorGrid;
import org.junit.jupiter.api.Test;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class TestCellPositionToPixelConverter {
@Test
public void illustratedSample() {
IColorGrid grid = newGridFromString(String.join("\n",
"R--B",
"----",
"Y--G")
);
CellPositionToPixelConverter converter = getConverter(
grid, new Rectangle2D.Double(30, 30, 340, 240), 30);
Rectangle2D expected = new Rectangle2D.Double(215, 130, 47.5, 40);
assertEquals(expected, converter.getBoundsForCell(new CellPosition(1, 2)));
}
/////////////////////////////
// Helper methods
/////////////////////////////
static CellPositionToPixelConverter getConverter(GridDimension grid, Rectangle2D rect, double margin) {
try {
Constructor<?> constructor = CellPositionToPixelConverter.class.getConstructor(
GridDimension.class, Rectangle2D.class, double.class
);
// Check that the constructor is public
assertTrue(Modifier.isPublic(constructor.getModifiers()),
"The constructor CellPositionToPixelConverter(IColorGrid, Rectangle2D, double)"
+ " should be public");
// Create a new object using the constructor and return it
return (CellPositionToPixelConverter) constructor.newInstance(grid, rect, margin);
} catch (NoSuchMethodException e) {
fail("Could not find the constructor CellPositionToPixelConverter(IColorGrid, Rectangle2D, " +
"double) in the CellPositionToPixelConverter class");
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
throw new IllegalStateException("Should not be possible to reach this point");
}
static IColorGrid newGridFromString(String stringGrid) {
return new IColorGrid() {
private String[] lines = stringGrid.split("\n");
@Override
public int rows() {
return this.lines.length;
}
@Override
public int cols() {
return this.lines[0].length();
}
@Override
public Color get(CellPosition pos) {
if (pos.row() < 0 || pos.row() >= this.rows()) {
throw new IllegalArgumentException("Row out of bounds");
}
if (pos.col() < 0 || pos.col() >= this.cols()) {
throw new IllegalArgumentException("Column out of bounds");
}
return switch (this.lines[pos.row()].charAt(pos.col())) {
case 'R' -> Color.RED;
case 'G' -> Color.GREEN;
case 'B' -> Color.BLUE;
case 'Y' -> Color.YELLOW;
default -> null;
};
}
@Override
public void set(CellPosition pos, Color color) {
// ignore
}
@Override
public java.util.List<CellColor> getCells() {
List<CellColor> a = new ArrayList<>();
for (int r = 0; r < this.rows(); r++) {
for (int c = 0; c < this.cols(); c++) {
Color color = this.get(new CellPosition(r, c));
a.add(new CellColor(new CellPosition(r, c), color));
}
}
return a;
}
};
}
}
package no.uib.inf101.gridview;
// NOTE: This file does NOT demonstrate a good way to write a test!
// Most tests in this file are written in a way that makes it possible
// to test HOW the problem is solved instead of testing simply THAT
// the problem is solved. This is done to be able to test that the
// detailed instructions for the assignment are followed, and could
// provide hints in an educational setting in case something is off.
//
// In a real project, you should not test private methods, but instead
// test public or package-private methods. Test *what* the code does,
// not *how* it does it (like we do here).
import no.uib.inf101.colorgrid.*;
import org.junit.jupiter.api.Test;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import static org.junit.jupiter.api.Assertions.*;
public class TestGridView {
@Test
public void preferredSize() {
GridView view = newGridView(null);
assertNotNull(view, "Unable to create a new GridView object");
assertNotNull(view.getPreferredSize(), "PreferredSize should not be null");
assertEquals(400, view.getPreferredSize().width);
assertEquals(300, view.getPreferredSize().height);
}
@Test
public void drawCellsIllustratedSample() {
// Sample case from assignment text
IColorGrid sampleGrid = TestCellPositionToPixelConverter.newGridFromString(String.join("\n",
"R--B",
"----",
"Y--G"
));
Rectangle2D rect = new Rectangle2D.Double(30, 30, 340, 240);
double margin = 30;
RecordGraphics2D record = singleDrawCellsRun(sampleGrid, rect, margin);
// Check that the correct number of calls were made
assertEquals(12, record.getFillRecordShapes().size(),
"The drawCells method call draw 12 rectangles in the illustrated sample case");
assertColorIsDrawnOnceAt(record, Color.RED, new Rectangle2D.Double(60, 60, 47.5, 40));
assertColorIsDrawnOnceAt(record, Color.BLUE, new Rectangle2D.Double(292.5, 60, 47.5, 40));
assertColorIsDrawnOnceAt(record, Color.YELLOW, new Rectangle2D.Double(60, 200, 47.5, 40));
assertColorIsDrawnOnceAt(record, Color.GREEN, new Rectangle2D.Double(292.5, 200, 47.5, 40));
}
@Test
public void drawCellsIllustratedSampleTweak() {
// Sample case from assignment text
IColorGrid sampleGrid = TestCellPositionToPixelConverter.newGridFromString(String.join("\n",
"R--B",
"----",
"Y--G"
));
Rectangle2D rect = new Rectangle2D.Double(40, 20, 342, 243);
double margin = 30;
RecordGraphics2D record = singleDrawCellsRun(sampleGrid, rect, margin);
// Check that the correct number of calls were made
assertEquals(12, record.getFillRecordShapes().size());
assertColorIsDrawnOnceAt(record, Color.RED, new Rectangle2D.Double(70, 50, 48, 41));
assertColorIsDrawnOnceAt(record, Color.BLUE, new Rectangle2D.Double(304, 50, 48, 41));
assertColorIsDrawnOnceAt(record, Color.YELLOW, new Rectangle2D.Double(70, 192, 48, 41));
assertColorIsDrawnOnceAt(record, Color.GREEN, new Rectangle2D.Double(304, 192, 48, 41));
}
@Test
public void drawCellsSingleCell() {
// Sample case from assignment text
IColorGrid sampleGrid = TestCellPositionToPixelConverter.newGridFromString("R");
Rectangle2D rect = new Rectangle2D.Double(20, 30, 40, 50);
double margin = 10;
RecordGraphics2D record = singleDrawCellsRun(sampleGrid, rect, margin);
// Check that the correct number of calls were made
assertEquals(1, record.getFillRecordShapes().size());
assertColorIsDrawnOnceAt(record, Color.RED, new Rectangle2D.Double(30, 40, 20, 30));
}
/////////////////////////////
// Helper methods
/////////////////////////////
private void assertColorIsDrawnOnceAt(RecordGraphics2D record, Color color, Rectangle2D expectedRectangle) {
int count = 0;
for (int i=0; i<record.getFillRecordColors().size(); i++) {
if (record.getFillRecordColors().get(i).equals(color)) {
assertEquals(expectedRectangle, record.getFillRecordShapes().get(i),
"Incorrect bounds for color "+ color
);
count++;
}
}
assertEquals(1, count, "There should be exactly one rectangle with color "+ color);
}
static GridView newGridView(IColorGrid grid) {
try {
Constructor<?> constructor = GridView.class.getConstructor(IColorGrid.class);
// Check that the constructor is public
assertFalse(Modifier.isPrivate(constructor.getModifiers()),
"The constructor GridView(IColorGrid) should not be private");
// Create a new object using the constructor and return it
return (GridView) constructor.newInstance(grid);
} catch (NoSuchMethodException e) {
if (grid != null) {
fail("Could not find the constructor GridView(IColorGrid) in the GridView class");
}
try {
Constructor<?> constructor = GridView.class.getConstructor();
return (GridView) constructor.newInstance();
} catch (NoSuchMethodException ex) {
fail("Could not find the constructor GridView() or GridView(IColorGrid) in the GridView class");
} catch (InvocationTargetException | InstantiationException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
throw new IllegalStateException("Should not be possible to reach this point");
}
public static RecordGraphics2D singleDrawCellsRun(
IColorGrid grid, Rectangle2D rect, double margin) {
// Get the "drawCells" method from the GridView class and invoke
// the method with the fake Graphics2D object
try {
Method drawCell = GridView.class.getDeclaredMethod("drawCells",
Graphics2D.class,
CellColorCollection.class,
CellPositionToPixelConverter.class
);
// Test that the method is static
assertTrue(Modifier.isStatic(drawCell.getModifiers()),
"The drawCells method should be static, and it should not use any instance variables");
// Test that the method is private
assertTrue(Modifier.isPrivate(drawCell.getModifiers()),
"The drawCells method should be private");
// Make the method accessible in case it is private
drawCell.setAccessible(true);
// Preparing a "fake" Graphics2D object that records stuff that
// happens to it instead of actually drawing anything
RecordGraphics2D g2 = new RecordGraphics2D();
// Invoke the method
drawCell.invoke(null, g2, grid, TestCellPositionToPixelConverter.getConverter(grid, rect, margin));
return g2;
} catch (NoSuchMethodException e) {
fail("Could not find the method drawCells(Graphics2D, CellColorCollection,"
+ " CellPositionToPixelConverter) in the GridView class");
} catch (InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
throw new IllegalStateException("Should not reach this point");
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment