Skip to content

Commit

Permalink
re-implemented using more idiomatic Xtend, added gradle build config
Browse files Browse the repository at this point in the history
  • Loading branch information
tobykurien authored and Andreas Pauley committed Apr 8, 2016
1 parent fed8e77 commit 89ca00e
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 110 deletions.
2 changes: 1 addition & 1 deletion xtend/tobykurien/.classpath
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="xtend-gen"/>
<classpathentry kind="con" path="org.eclipse.xtend.XTEND_CONTAINER"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="xtend-gen"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>
5 changes: 5 additions & 0 deletions xtend/tobykurien/.gitignore
@@ -0,0 +1,5 @@
bin
pricefile.txt
xtend-gen
build
.gradle
34 changes: 11 additions & 23 deletions xtend/tobykurien/README.md
@@ -1,33 +1,28 @@
Introduction
============

This is the Xtend implementation of the HollingBerries problem. Now I know you're rolling
your eyes thinking: "Oh gawd, another obscure language I've never heard of!", but hold on
a sec and hear me out. Xtend is not like your other typical languages. Here are the highlights:
This is the Xtend implementation of the HollingBerries problem. Now I know you're rolling your eyes thinking: "Oh gawd, another obscure language I've never heard of!", but hold on a sec and hear me out. Xtend is different because it is a *transpiler*. Here are the highlights:

* Xtend is essentially a pre-processor for Java. So it compiles the code to Java source files,
which are then compiled by the Java compiler.
* Xtend is essentially a pre-processor for Java. So it compiles the code to Java source files, which are then compiled by the Java compiler.

* Java source is valid Xtend source. This means Xtend is simply additions to the Java language to
make it prettier and more functional.
* Xtend source is mostly Java with some boilerplate removed and a lot of syntactic sugar added.

* The learning curve is thus not steep. You simply use the bits of Xtend that make things easier for you.
* The learning curve is thus not steep (~1 hour for a Java dev). You simply use the bits of Xtend that make things easier for you.

* Xtend is an Eclipse project and thus has deep Eclipse integration and tooling support. You can even debug
Xtend code by stepping through Xtend code.
* Xtend is an Eclipse project and thus has deep Eclipse integration and tooling support (IntelliJ 15+ is also supported). You can even debug Xtend code by stepping through Xtend code.

* Read more here: http://xtend-lang.org

How to run this Xtend version
=============================

* Install Eclipse and Xtend. See here for details: http://www.eclipse.org/xtend/#download
* Install [gradle][]

* Import the project into Eclipse (File > Import > Existing projects into workspace)
* Change to this directory in a terminal

* Hit the "Run" button. You won't see any output in the console.
* Run:

* Check that the pricefile.txt output was created in the project root
> $ gradle run
* Verify test passed by running this in the command-line from the project root:

Expand All @@ -37,13 +32,6 @@ How to run this Xtend version
About the Code
==============

This is a similar implementation to my Ruby implementation, but instead of the elaborate config data, I used the
powerful switch functionality of Xtend to map the product codes to markups and sell_by dates. Also, unlike the Ruby
version, as is common in Java, I created a data object to hold the CSV data. This allows code-completion and other
advantages of statically-typed languages to come through.

I made use of Xtend's functional syntax, extention methods, and string templating, which makes the code look like
Ruby or some other dynamic language. Despite this, everything is statically typed, so it's like the best of both
worlds. This is one of the attractions of Xtend.

This is a similar implementation to my Ruby implementation, but instead of the elaborate config data, I used the powerful switch functionality of Xtend to map the product codes to markups and sell_by dates. Also, unlike the Ruby version, as is common in Java, I created a data object to hold the CSV data. This allows code-completion and other advantages of statically-typed languages to come through.

I made use of Xtend's functional syntax, extention methods, and string templating, which makes the code look like Ruby or some other dynamic language. Despite this, everything is statically typed, so it's like the best of both worlds. This is one of the attractions of Xtend.
49 changes: 49 additions & 0 deletions xtend/tobykurien/build.gradle
@@ -0,0 +1,49 @@
apply plugin: 'application'

// Allows generating an Eclipse project via 'gradle eclipse'
apply plugin: 'eclipse'
apply plugin: "org.xtext.xtend"

mainClassName = "HollingBerries"

repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}

dependencies {
compile 'org.eclipse.xtend:org.eclipse.xtend.lib:2.9.0'
}

eclipse {
project {
natures 'org.eclipse.xtext.ui.shared.xtextNature'
buildCommand 'org.eclipse.xtext.ui.shared.xtextBuilder'
}
}

sourceSets {
main {
java {
srcDir 'src/'
}
resources {
srcDir 'src/resources'
}
}
}

buildscript {
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}

dependencies {
classpath "org.xtext:xtext-gradle-plugin:1.0.2"
}
}
175 changes: 89 additions & 86 deletions xtend/tobykurien/src/HollingBerries.xtend
@@ -1,102 +1,105 @@
import java.io.FileReader
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.Date
import org.eclipse.xtend.lib.Data
import static HollingBerries.*
import org.eclipse.xtend.lib.annotations.Data

import static extension com.google.common.io.CharStreams.*
import java.io.FileWriter
import java.util.Calendar

// Class to hold product data
@Data class Product {
int supplierId
int productCode
String description
Date deliveryDate
int price
int quantity
int supplierId
int productCode
String description
Date deliveryDate
int price
int quantity
}

@Data class ProduceData {
IntegerRange codes
double markup
int shelfLifeDays
}

// Main class
class HollingBerries {
// supplier ratings
val troubleSuppliers = newArrayList(32, 101)
val premiumSuppliers = newArrayList(219, 204)
// Data for each type of produce: code range, markup, shelf life in days
val produceData = #{
"Apples" -> new ProduceData((1100 .. 1199), 1.40, 14),
"Bananas" -> new ProduceData((1200 .. 1299), 1.35, 5),
"Berries" -> new ProduceData((1300 .. 1399), 1.55, 7),
"Other" -> new ProduceData(null, 1.5, 7)
}

// compute the selling price based on product type and supplier
def getSellPrice(Product p) {
var markup = switch p {
case (1100..1199).contains(p.productCode): 40
case (1200..1299).contains(p.productCode): 35
case (1300..1399).contains(p.productCode): 55
default: 50
}

// adjust for suppliers
sanePrice(switch p {
case troubleSuppliers.contains(p.supplierId):
(p.price * (1 + markup/100.0) - 200) / 100.0
case premiumSuppliers.contains(p.supplierId):
Math::ceil(p.price * (1 + (markup+10)/100.0) / 100.0)
default:
p.price * (1 + markup/100.0) / 100.0
})
}
// supplier ratings
val troubleSuppliers = #[32, 101]
val premiumSuppliers = #[219, 204]

// compute sell by date based on product type and supplier
def getSellBy(Product p) {
var adj = switch p {
case (1100..1199).contains(p.productCode): 14
case (1200..1299).contains(p.productCode): 5
default: 7
}

// adjust for suppliers
if (troubleSuppliers.contains(p.supplierId)) {
val inDateFormat = new SimpleDateFormat("\"yyyy/MM/dd\"")
val outdateFormat = new SimpleDateFormat("yyyy/MM/dd")

def getProduceData(Product p) {
produceData.values.findFirst[ codes == null || codes.contains(p.productCode) ]
}

// compute the selling price based on product type and supplier
def getSellPrice(Product p) {
var markup = p.produceData.markup

// adjust for suppliers
switch p {
case troubleSuppliers.contains(p.supplierId):
(p.price * markup - 200.0) / 100.0
case premiumSuppliers.contains(p.supplierId):
Math::ceil(p.price * (markup + 0.1) / 100.0)
default:
p.price * markup / 100.0
}
}

// compute sell by date based on product type and supplier
def getSellBy(Product p) {
var adj = p.produceData.shelfLifeDays

// adjust for suppliers
if (troubleSuppliers.contains(p.supplierId)) {
adj = adj - 3
}

new Date(p.deliveryDate.time + adj*1000*60*60*24)
}
}

new Date(p.deliveryDate.time + adj * 1000 * 60 * 60 * 24)
}

// generate the label printer lines for each product
def String processProduct(Product p) {
if (p.quantity <= 0) return ""
(0 .. p.quantity - 1).join("", [
var sellPrice = Math.max(p.getSellPrice, 0.0)
var sellBy = p.getSellBy
var desc = p.description.substring(1, 32)
'''R«String::format("% 8.2f", sellPrice)»«outdateFormat.format(sellBy)»«desc»
'''.toString
])
}

// make sure prices are sane
def sanePrice(double price) {
if (price < 0) 0 else price
}
// processing loop to read CSV and write label printer file
def process(String sourceCsv, String outputCsv) {
val out = new FileWriter(outputCsv)
new FileReader(sourceCsv).readLines.drop(1).forEach [
val cols = it.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)", -1).iterator
val product = new Product(
Integer::parseInt(cols.next),
Integer::parseInt(cols.next),
cols.next,
inDateFormat.parse(cols.next),
Integer::parseInt(cols.next),
Integer::parseInt(cols.next)
)
out.write(processProduct(product))
]
out.close
}

// generate the label printer lines for each product
def String processProduct(Product p) {
if (p.quantity == 0) return ""
(0..p.quantity-1).join("", [
var sellPrice = p.getSellPrice
var sellBy = p.getSellBy
var desc = p.description.substring(1,32)

var sdf = new SimpleDateFormat("yyyy/MM/dd")
'''R«String::format("% 8.2f", sellPrice)»«sdf.format(sellBy)»«desc»
'''.toString
])
}

// processing loop to read CSV and write label printer file
def process(String sourceCsv, String outputCsv) {
val out = new FileWriter(outputCsv)
new FileReader(sourceCsv).readLines.drop(1).forEach[
val cols = it.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)", -1).iterator
val product = new Product(
Integer::parseInt(cols.next),
Integer::parseInt(cols.next),
cols.next,
new SimpleDateFormat("\"yyyy/MM/dd\"").parse(cols.next),
Integer::parseInt(cols.next),
Integer::parseInt(cols.next)
)
out.write(processProduct(product))
]
out.close
}

def static void main(String[] args) {
new HollingBerries().process("../../produce.csv", "pricefile.txt")
}
def static void main(String[] args) {
new HollingBerries().process("../../produce.csv", "pricefile.txt")
}
}

0 comments on commit 89ca00e

Please sign in to comment.