Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
/.idea/
/target
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# BinaryTattoo
Transform text into it's binary representation (ASCII) in many ways with this lovely little cmd line tool!

The goal of this project is to provide binary representation of the ASCII character code points. Basic usage is to print them in vertical or horizontal direction, for making a tattoo out of it.
# Usage
* '-i' (required) - input to be binarized (string; might some wise sentence, like 'I never don't give up')
* '-s' (optional) - separator you want to use in between the binary words (I discourage to use '0' or '1', for obvious reasons). Defaults to ' ' (single space)
* '-d' (optional) - the direction in which the binarized tattoo will be printed. Available options in 2D world are: 'HORIZONTAL' and 'VERTICAL'. Sorry non-euclideans...

Have any suggestions? Well, I don't care, this is my baby and I will implement only the things I want (or even less, due to procrastination)
67 changes: 67 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,77 @@
<target>8</target>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<mainClass>Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>

<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.4</version>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.4</version>
</dependency>


</dependencies>

Expand Down
43 changes: 43 additions & 0 deletions src/main/java/BinaryParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import lombok.extern.slf4j.Slf4j;
import model.BinaryLetter;
import model.BinaryWord;

@Slf4j
public class BinaryParser {

public static BinaryWord parseBinaryWord(String input) {
log.info("Parsing input: {}", input);
long startTime = System.currentTimeMillis();

BinaryWord word = new BinaryWord();
for (char c : input.toCharArray()) {
word.appendLetter(parseBinaryLetter(c));
}

long duration = System.currentTimeMillis() - startTime;
log.info("Parsing complete, result: {} (took: {}ms)", word, duration);
return word;
}

private static BinaryLetter parseBinaryLetter(char input) {
int divider = 1;

// Find closest binary incrementation, i.e. 8, 16, 32 etc...
while (input > divider) {
divider *= 2;
}

BinaryLetter.Builder letterBuilder = new BinaryLetter.Builder();
while (divider > 0) {
if (input >= divider) {
letterBuilder.appendBit(true);
input -= divider;
} else {
letterBuilder.appendBit(false);
}
divider /= 2;
}

return letterBuilder.build();
}
}
29 changes: 29 additions & 0 deletions src/main/java/BinaryTattoo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import lombok.extern.slf4j.Slf4j;
import model.BinaryWord;
import model.TattooData;
import model.phrase.BinaryPhrase;
import model.phrase.BinaryPhraseBuilder;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class BinaryTattoo {

private static final String DEFAULT_SEPARAOR = " ";

public static String toBinaryString(TattooData tattooData) {

final List<BinaryWord> words = Stream.of(tattooData.tattooString().split(DEFAULT_SEPARAOR))
.map(BinaryParser::parseBinaryWord).collect(Collectors.toList());

final BinaryPhrase phrase = new BinaryPhraseBuilder()
.words(words)
.separator(tattooData.separator())
.direction(tattooData.printDirection())
.build();

return phrase.toString();
}
}
88 changes: 88 additions & 0 deletions src/main/java/CmdLineTattooParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import lombok.extern.slf4j.Slf4j;
import model.TattooData;
import model.phrase.PrintDirection;
import org.apache.commons.cli.*;

import java.util.Arrays;
import java.util.stream.Collectors;

@Slf4j
public class CmdLineTattooParser {

public static final String INPUT_FLAG = "i";
public static final String INPUT_FLAG_DESCRIPTION = "input string to be binarized";

public static final String SEPARATOR_FLAG = "s";
public static final String SEPARATOR_FLAG_DESCRIPTION = "word separator (default: space, i.e. ' ')";
public static final String DEFAULT_SEPARATOR = " ";

public static final String DIRECTION_FLAG = "d";
public static final String DIRECTION_FLAG_DESCRIPTION = "print direction (default: vertical)";
public static final PrintDirection DEFAULT_DIRECTION = PrintDirection.VERTICAL;

public TattooData readArgs(String[] args) throws ParseException {
Options cmdOptions = buildOptions();
try {
CommandLine parsedArgs = new DefaultParser().parse(cmdOptions, args);

return parseTattooData(parsedArgs);
} catch (IllegalArgumentException ex) {
String generalMessage = String.format("Unable to parse input, cause: %s", ex.getMessage());
printHelp(generalMessage, cmdOptions);
throw new ParseException(generalMessage);
}
}

private Options buildOptions() {
Options cmdOptions = new Options();
cmdOptions.addOption(new Option(INPUT_FLAG, true, INPUT_FLAG_DESCRIPTION));
cmdOptions.addOption(new Option(SEPARATOR_FLAG, true, SEPARATOR_FLAG_DESCRIPTION));
cmdOptions.addOption(new Option(DIRECTION_FLAG, true, DIRECTION_FLAG_DESCRIPTION));
return cmdOptions;
}

private TattooData parseTattooData(CommandLine parsedArgs) {
String tattooString = parsedArgs.getOptionValue(INPUT_FLAG);
if (tattooString == null || tattooString.isEmpty()) {
throw new IllegalArgumentException("Input tattoo string cannot be empty!");
}
if (tattooString.length() > 100000) {
throw new IllegalArgumentException("Seriously? You don't have long enough body for this shit");
}

log.trace("Provided tattooString is correct: '{}'", tattooString);
TattooData tattooData = new TattooData(tattooString);

String separator = parsedArgs.getOptionValue(SEPARATOR_FLAG);
if (separator == null || separator.isEmpty()) {
log.warn("You've provided empty separator, defaulting to '{}'", DEFAULT_SEPARATOR);
tattooData.separator(DEFAULT_SEPARATOR);
} else {
if (separator.length() > 100) {
log.warn("Seriously? Such long separation should only be between you and this program..");
tattooData.separator(DEFAULT_SEPARATOR);
} else {
log.trace("Provided separator is correct: '{}'", separator);
tattooData.separator(separator);
}
}

try {
String directionString = parsedArgs.getOptionValue(DIRECTION_FLAG);
tattooData.printDirection(PrintDirection.valueOf(directionString));
log.trace("Provided print direction is correct: '{}'", directionString);
} catch (IllegalArgumentException ex) {
log.error("Invalid print direction specified, allowed values: {}, defaulting to: {}",
Arrays.stream(PrintDirection.values()).map(PrintDirection::toString).collect(Collectors.joining(",")),
DEFAULT_DIRECTION);
tattooData.printDirection(DEFAULT_DIRECTION);
}

return tattooData;
}

private void printHelp(String generalMessage, Options options) {
log.error(generalMessage);
new HelpFormatter().printHelp("[packaged_jar]", options);
}
}
14 changes: 14 additions & 0 deletions src/main/java/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import model.TattooData;
import org.apache.commons.cli.ParseException;

public class Main {

public static void main(String[] args) {
try {
TattooData tattooData = new CmdLineTattooParser().readArgs(args);
System.out.println(BinaryTattoo.toBinaryString(tattooData));
} catch (ParseException ex) {
// CmdLineTattooParser print help message, we just want to 'Enjoy the silence' here
}
}
}
69 changes: 69 additions & 0 deletions src/main/java/model/BinaryLetter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package model;

import java.util.ArrayDeque;
import java.util.Queue;

public class BinaryLetter {

private final Queue<Boolean> bits;

private BinaryLetter(Builder b) {
this.bits = b.bits;
}

public boolean hasNextBit() {
return !bits.isEmpty();
}

public boolean pollNextBit() {
Boolean bit = bits.poll();
if (bit == null) {
throw new IllegalArgumentException("No bits left!");
}
return bit;
}

/**
* Return the number of bits, that consists to the letter
*
* @return number of bits
*/
public int length() {
return bits.size();
}

@Override
public String toString() {
return bits.stream()
.map(bit -> bit ? "1" : "0")
.reduce(String::concat)
.orElse("!");
}

public static class Builder {
private int bitRange = 8;
private final Queue<Boolean> bits = new ArrayDeque<>();

public Builder bitRange(int bitRange) {
this.bitRange = bitRange;
return this;
}

/**
* Sets the next bit, from left to right.
*
* @param bit true (1), or false (0)
*/
public Builder appendBit(boolean bit) {
bits.add(bit);
return this;
}

public BinaryLetter build() throws IllegalArgumentException {
if (bitRange < 1 || bits.size() != bitRange) {
throw new IllegalArgumentException("Invalid bit range!");
}
return new BinaryLetter(this);
}
}
}
42 changes: 42 additions & 0 deletions src/main/java/model/BinaryWord.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package model;

import java.util.ArrayDeque;
import java.util.Queue;

public class BinaryWord {

private final Queue<BinaryLetter> letters = new ArrayDeque<>();

public void appendLetter(BinaryLetter letter) {
letters.add(letter);
}

public boolean hasNextLetter() {
return !letters.isEmpty();
}

public BinaryLetter pollNextLetter() {
BinaryLetter letter = letters.poll();
if (letter == null) {
throw new IllegalArgumentException("No letters left!");
}
return letter;
}

/**
* Return the number of bits, that consists to the word
*
* @return number of bits
*/
public int length() {
return letters.stream().map(BinaryLetter::length).reduce(Integer::sum).orElse(0);
}

@Override
public String toString() {
return letters.stream()
.map(BinaryLetter::toString)
.reduce((letter1, letter2) -> letter1 + " " + letter2)
.orElse("EMPTY");
}
}
Loading