Adding ability to import external shape files via shape generator.

This commit is contained in:
Rob Greene 2018-07-14 23:25:55 -05:00
parent 53b8575d3b
commit 35b02d96dc
8 changed files with 198 additions and 0 deletions

View File

@ -0,0 +1,98 @@
package io.github.applecommander.bastools.api.shapes;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.IntStream;
import io.github.applecommander.bastools.api.utils.Converters;
/**
* Allow the import of an external shape. Processing is very dependent on
* being invoked in the "correct" manner!
* <p>
* Prototype code:
* <pre>
* ; Read in external shape table: configure first and then "import" processes file.
* .external characters
* type=bin
* shapes=1-96
* import=imperator.bin
* </pre>
*/
public class ExternalShapeImporter {
private ShapeTable destination;
private String firstShapeLabel;
private Function<String,ShapeTable> importer = this::importShapeTableFromBinary;
private IntStream intStream = null;
public ExternalShapeImporter(ShapeTable destination, String firstShapeLabel) {
this.destination = destination;
this.firstShapeLabel = firstShapeLabel;
}
public void process(String line) {
Objects.requireNonNull(line);
String[] parts = line.split("=");
if (parts.length != 2) {
throw new RuntimeException(String.format(".external fields require an assignment for '%s'", line));
}
switch (parts[0].toLowerCase()) {
case "type":
switch (parts[1].toLowerCase()) {
case "bin":
importer = this::importShapeTableFromBinary;
break;
case "src":
importer = this::importShapeTableFromSource;
break;
default:
throw new RuntimeException(String.format("Unknown import type specified: '%s'", line));
}
break;
case "shapes":
intStream = Converters.toIntStream(parts[1]);
break;
case "import":
ShapeTable temp = importer.apply(parts[1]);
// Shapes in Applesoft are 1 based but Java List object is 0 based...
intStream.map(n -> n-1).mapToObj(temp.shapes::get).forEach(this::importShape);
break;
default:
throw new RuntimeException(String.format("Unknown assignment '%s' for .external", line));
}
}
public ShapeTable importShapeTableFromBinary(String filename) {
// FIXME May need access to Configuration for these nested files?
try {
Objects.requireNonNull(intStream, ".external requires that 'shapes' is specified");
return ShapeTable.read(Paths.get(filename));
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
public ShapeTable importShapeTableFromSource(String filename) {
// FIXME May need access to Configuration for these nested files?
try {
Objects.requireNonNull(intStream, ".external requires that 'shapes' is specified");
return ShapeGenerator.generate(Paths.get(filename));
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
public void importShape(Shape shape) {
if (firstShapeLabel != null) {
VectorShape vshape = new VectorShape(firstShapeLabel);
vshape.vectors.addAll(shape.toVector().vectors);
destination.shapes.add(vshape);
firstShapeLabel = null;
} else {
destination.shapes.add(shape);
}
}
}

View File

@ -1,5 +1,13 @@
package io.github.applecommander.bastools.api.shapes;
/**
* Represents a single Applesoft shape. Note that the interface is mostly useful to get at the
* bitmap or vector shapes. This also implies that these implementations need to transform between
* eachother!
*
* @see BitmapShape
* @see VectorShape
*/
public interface Shape {
/** Indicates if this shape is empty. */
public boolean isEmpty();

View File

@ -44,6 +44,10 @@ public class ShapeGenerator {
st.shapes.add(bitmapShape);
shapeConsumer = bitmapShape::appendBitmapRow;
break;
case ".external":
ExternalShapeImporter importer = new ExternalShapeImporter(st, label);
shapeConsumer = importer::process;
break;
default:
if (line.length() == 0) {
// do nothing

View File

@ -16,7 +16,12 @@ import java.util.stream.Collectors;
import io.github.applecommander.bastools.api.utils.Streams;
/**
* Represents an Applesoft shape table. Note that this direct class is somewhat useless,
* except for the I/O routines. Access the individual shapes via the {@code #shapes} list.
*/
public class ShapeTable {
/** Read an existing Applesoft shape table binary file. */
public static ShapeTable read(byte[] data) {
Objects.requireNonNull(data);
ShapeTable shapeTable = new ShapeTable();

View File

@ -1,5 +1,8 @@
package io.github.applecommander.bastools.api.utils;
import java.util.Arrays;
import java.util.stream.IntStream;
public class Converters {
private Converters() { /* Prevent construction */ }
@ -19,10 +22,39 @@ public class Converters {
}
}
/**
* Convert a string to a boolean value allowing for "true" or "yes" to evaluate to Boolean.TRUE.
*/
public static Boolean toBoolean(String value) {
if (value == null) {
return null;
}
return "true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value);
}
/**
* Supports entry of values in ranges or comma-separated lists and combinations thereof.
* <ul>
* <li>Range: <code>m-n</code> where m<n.</li>
* <li>Distinct values: <code>a,b,c,d</code>.</li>
* <li>Single value: <code>x</code></li>
* <li>Combination: <code>m-n;a,b,c,d;x</code>.</li>
* </ul>
*/
public static IntStream toIntStream(String values) {
IntStream stream = IntStream.empty();
for (String range : values.split(";")) {
if (range.contains("-")) {
String[] parts = range.split("-");
int low = Integer.parseInt(parts[0]);
int high = Integer.parseInt(parts[1]);
stream = IntStream.concat(stream, IntStream.rangeClosed(low, high));
} else {
stream = IntStream.concat(stream,
Arrays.asList(range.split(",")).stream().mapToInt(Integer::parseInt));
}
}
return stream;
}
}

View File

@ -0,0 +1,45 @@
package io.github.applecommander.bastools.api.utils;
import static org.junit.Assert.*;
import org.junit.Test;
public class ConverterTest {
@Test
public void testToInteger() {
assertEquals(0x1000, Converters.toInteger("0x1000").intValue());
assertEquals(0x1000, Converters.toInteger("$1000").intValue());
assertEquals(1000, Converters.toInteger("1000").intValue());
}
@Test
public void testToBoolean() {
assertTrue(Converters.toBoolean("true"));
assertTrue(Converters.toBoolean("True"));
assertTrue(Converters.toBoolean("YES"));
assertFalse(Converters.toBoolean("faLse"));
assertFalse(Converters.toBoolean("No"));
assertFalse(Converters.toBoolean("notreally"));
}
@Test
public void testToIntStream_Range() {
final int[] expected = { 4, 5, 6, 7, 8 };
assertArrayEquals(expected, Converters.toIntStream("4-8").toArray());
}
@Test
public void testToIntStream_List() {
final int[] expected314159 = { 3, 1, 4, 1, 5, 9 };
assertArrayEquals(expected314159, Converters.toIntStream("3,1,4,1,5,9").toArray());
final int[] expected7 = { 7 };
assertArrayEquals(expected7, Converters.toIntStream("7").toArray());
}
@Test
public void testToIntStream_Complex() {
final int[] expected = { 1, 5,6,7, 9, 2,3,4, 8 };
assertArrayEquals(expected, Converters.toIntStream("1;5-7;9;2-4;8").toArray());
}
}

BIN
samples/imperator.bin Normal file

Binary file not shown.

View File

@ -109,3 +109,9 @@
.xx
x*x
xx.
; "]IMPERATOR" font from Beagle Bros. "Apple Mechanic"
.external characters
type=bin
shapes=1-96
import=imperator.bin