Skip to content

How not to do ebooks and customer service

The ever-observant Pro JavaFX Platform. So far, the content of the book is very good. The problem I have is with it's packaging.

The main reasons to buy ebooks (e.g., PDF) over dead-tree books are that you can keyword search them and cut-and-paste the code examples. However, Apress (a subsidiary of Springer-Verlag) has decided to disable this. Most of the text can be cut-and-pasted, but the code samples cannot. None of the text is searchable.

When I contacted Apress about this, I got this courteous but useless response:

Hi Phil,

Thanks for contacting Apress Customer Support.

We're sorry about the inconvenience when trying to cut and paste code samples. One way around this though is to use the snapshot tool in Adobe Acrobat.

Using the snapshot tool, simply drag the tool around the area of code you want to cut and paste and take a snapshot and then you'll be able to past that code into word as an image.

Please contact us if you have questions concerning any of our other books.

Thanks,
Apress Customer Support

Since NetBeans has the new "paste an image to text feature", this is great (sarcasm). The decision to publish their books with these restrictions (including password protection) was an intentional one, so there should be some explicit policy on why this is that their customer support reps can refer to — otherwise they're stuck making useless recommendations like the above and discouraging customers from buying Apress ebooks in the future. But, it's probably not very appealing for them to explictly say "we don't trust our customers, so we've decided to annoy the honest ones."

I have a few Pragmatic Press books, which do allow full searching and cut and paste. Their model of ebook publishing is that they trust their customers and simply generate a PDF for you that has your name and email on each page. Totally non-intrusive to honest users, and enough to discourage marginally dishonest ones. A much better model, and one I hope Apress will soon adopt.

Snow Leopard: what I really like

Snow Leopard isn't the typical big leap most MacOS X versions have been, and I think it's smart marketing on the part of Apple to name it a variation on the previous version than waste a whole cat on it. Most of the features I don't really understand (Grand Central Dispatch) or care about (Exchange support), but three have really stood out for me:

Offline printing works! I typically have my MBP disconnected when I send something to print, and then plug it in later. This never really worked correctly in Leopard, with the vague "Operation error printer-not-supported-no-one-understands-this-message" popup. A combination of plugging and unplugging the printer and attempting to resume the print job usually did the trick. Now it just works — amazing!

Annotations in Preview Probably the reason 99% of people buy Acrobat Pro is so they can annotate PDFs. Now you can do it in Preview

Faster AirPort connecting I don't know if this is an update to AirPort or my new AirPort Express (hooked into my stereo, great!), but connecting is really fast now after opening my MBP, no more Try Again or waiting.

Overall, just a lot of fixes and polish. Well done.

Java: Localized number formatting

The other day, I had an NLS bug to respond to, and realized I didn't know how numbers were formatted for any locales other than English and French. Quick, to the JVM:

ar    3,141.59
   ar_AE    3,141.59
   ar_BH    3,141.59
   ar_DZ    3,141.59
   ar_EG    3,141.59
   ar_IQ    3,141.59
   ar_JO    3,141.59
   ar_KW    3,141.59
   ar_LB    3,141.59
   ar_LY    3,141.59
   ar_MA    3,141.59
   ar_OM    3,141.59
   ar_QA    3,141.59
   ar_SA    3,141.59
   ar_SD    3,141.59
   ar_SY    3,141.59
   ar_TN    3,141.59
   ar_YE    3,141.59
be    3 141,59
   be_BY    3 141,59
bg    3 141,59
   bg_BG    3 141,59
ca    3.141,59
   ca_ES    3.141,59
cs    3 141,59
   cs_CZ    3 141,59
da    3.141,59
   da_DK    3.141,59
de    3.141,59
   de_AT    3.141,59
   de_CH    3'141.59
   de_DE    3.141,59
   de_LU    3.141,59
el    3.141,59
   el_CY    3.141,59
   el_GR    3.141,59
en    3,141.59
   en_AU    3,141.59
   en_CA    3,141.59
   en_GB    3,141.59
   en_IE    3,141.59
   en_IN    3,141.59
   en_MT    3,141.59
   en_NZ    3,141.59
   en_PH    3,141.59
   en_SG    3,141.59
   en_US    3,141.59
   en_ZA    3,141.59
es    3.141,59
   es_AR    3.141,59
   es_BO    3.141,59
   es_CL    3.141,59
   es_CO    3.141,59
   es_CR    3,141.59
   es_DO    3,141.59
   es_EC    3.141,59
   es_ES    3.141,59
   es_GT    3,141.59
   es_HN    3,141.59
   es_MX    3,141.59
   es_NI    3,141.59
   es_PA    3,141.59
   es_PE    3.141,59
   es_PR    3,141.59
   es_PY    3.141,59
   es_SV    3,141.59
   es_US    3,141.59
   es_UY    3.141,59
   es_VE    3.141,59
et    3 141,59
   et_EE    3 141,59
fi    3 141,59
   fi_FI    3 141,59
fr    3 141,59
   fr_BE    3.141,59
   fr_CA    3 141,59
   fr_CH    3'141.59
   fr_FR    3 141,59
   fr_LU    3 141,59
ga    3,141.59
   ga_IE    3,141.59
   hi_IN    ?,???.??
hr    3.141,59
   hr_HR    3.141,59
hu    3 141,59
   hu_HU    3 141,59
in    3.141,59
   in_ID    3.141,59
is    3.141,59
   is_IS    3.141,59
it    3.141,59
   it_CH    3'141.59
   it_IT    3.141,59
iw    3,141.59
   iw_IL    3,141.59
ja    3,141.59
   ja_JP    3,141.59
   ja_JP_JP    3,141.59
ko    3,141.59
   ko_KR    3,141.59
lt    3 141,59
   lt_LT    3 141,59
lv    3 141,59
   lv_LV    3 141,59
mk    3.141,59
   mk_MK    3.141,59
ms    3,141.59
   ms_MY    3,141.59
mt    3,141.59
   mt_MT    3,141.59
nl    3.141,59
   nl_BE    3.141,59
   nl_NL    3.141,59
no    3 141,59
   no_NO    3 141,59
   no_NO_NY    3 141,59
pl    3 141,59
   pl_PL    3 141,59
pt    3.141,59
   pt_BR    3.141,59
   pt_PT    3.141,59
ro    3.141,59
   ro_RO    3.141,59
ru    3 141,59
   ru_RU    3 141,59
sk    3 141,59
   sk_SK    3 141,59
sl    3.141,59
   sl_SI    3.141,59
sq    3.141,59
   sq_AL    3.141,59
sr    3.141,59
   sr_BA    3.141,59
   sr_CS    3.141,59
   sr_ME    3.141,59
   sr_RS    3.141,59
sv    3 141,59
   sv_SE    3 141,59
th    3,141.59
   th_TH    3,141.59
   th_TH_TH    ?,???.??
tr    3.141,59
   tr_TR    3.141,59
uk    3.141,59
   uk_UA    3.141,59
vi    3.141,59
   vi_VN    3.141,59
zh    3,141.59
   zh_CN    3,141.59
   zh_HK    3,141.59
   zh_SG    3,141.59
   zh_TW    3,141.59

Code for generating it:

import java.text.NumberFormat;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;

public class Class1 {
   public static void main(String[] args) {
       Set list = new TreeSet();
       for (Locale locale : Locale.getAvailableLocales())
           list.add(locale + "\t" + NumberFormat.getInstance(locale).format(3141.59));
       for (String s : list) {
           if (s.contains("_")) System.out.println("\t" + s);
           else System.out.println(s);
       }
   }
}

JavaFX: binding property values to anonymous function calls

Summary: When binding to anonymous functions in JavaFX, make sure you bind to the value of evaluating the function rather than the function itself.

One of the most useful aspects of JavaFX is property binding. This allows a more declarative description of how the various UI and model components interact, and frees the user from needing to manually maintain the syncronization between them. A powerful aspect of this is to bind a property value to a value derived from calling a function with other property values.

I'm working on a small cellular automata simulator (Game of Life), which involves displaying rectangles on the screen that have a state and a color, where the color is derived from the state. The code looks like:

function state2color(b:Boolean) { if (b) { Color.SEAGREEN; } else { Color.WHITESMOKE; }}
 
class Cell {
    var state:Boolean; 
    var color:Color = bind state2color(state);
}

Because 'color' is bound to the state2color function call, it's updated any time the value of 'state' is changed. However, state2color isn't used anywhere else, so I wanted to make it an anonymous function. The first thing I tried was this:

var color:Color = bind function() { if (state) { Color.SEAGREEN; } else { Color.WHITESMOKE; }};

but I got the error:

incompatible types:
found:    function():javafx.scene.paint.Color
required: javafx.scene.paint.Color

The problem here is that I'm trying to bind the 'color' property to the anonymous function, rather than binding the property to the value of _evaluating_ the anonymous function.

The simple fix is to add parens after the function declaration, indicating that it's the return value we want:

class Cell {
    var color:Color = bind function(){ if (state) { Color.SEAGREEN; } else { Color.WHITESMOKE; }}();
}

or alternatively:

class Cell {
    var color:Color = bind function(b){ if (b) { Color.SEAGREEN; } else { Color.WHITESMOKE; }}(state);
}

Java Map interface impl for composition

One of the mantras of object-oriented programming is that you should always favor composition over inheritance. However, it's always annoying when you want to implement Map but don't want to copy-paste-edit a bunch of methods that are just pass-through to the underlying map. This is code to do that, so I won't be tempted to just extend HashMap anymore.

  private final Map<String,String> m_map = new HashMap<String,String>();
 
  // begin Map impl
  public void clear(){ m_map.clear(); }
  public boolean containsKey(Object key){ return m_map.containsKey(key); }
  public boolean containsValue(Object value){ return m_map.containsValue(value); }
  public Set<Map.Entry<String,String>> entrySet(){ return m_map.entrySet(); }
  public boolean equals(Object o){ return m_map.equals(o); }
  public String get(Object key){ return m_map.get(key);}
  public int hashCode(){ return m_map.hashCode(); }
  public boolean isEmpty(){ return m_map.isEmpty(); }
  public Set<String> keySet(){ return m_map.keySet(); }
  public String put(String key, String value){ return m_map.put(key,value);}
  public void putAll(Map<? extends String,? extends String> m){ m_map.putAll(m) ;}
  public String remove(Object key){ return m_map.remove(key);}
  public int size(){ return m_map.size(); }
  public Collection<String> values(){ return m_map.values(); }
  // end Map impl

Books, January 2009

Thinking in Java by Bruce Eckel — I've literally been reading this book for 9 years. This was the book I learned Java from many years ago when I was a student, through the graciousness of Eckel making it available for free electronically. I accidentally bought two copies, so I now have one at work and one at home. I finally got around to reading the chapters on annotations, enums, inner classes, and generics.


Getting Things Done: The Art of Stress-Free Productivity by David Allen
— Mr. Allen, where have you been all my life? As he freely admits, a lot of the stuff in the book is common sense, but its the implementation that people get hung up on. I've started writing down everything I need to do, which alone has made me more productive with lower stress. Highly recommended.

The Little Schemer by Daniel P. Friedman and Matthias Felleisen — This series is one of the weirdest. The Socratic style is off-putting to some, including me, at least initially. Some of the text borders on cheesy, but I'm growing my appreciation of whimsy. I still don't understand they Y combinator, so I'm going to need to revisit this in a few months.

Practices of an Agile Developer: Working in the Real World (Pragmatic Programmers) (Jul 1, 2005) by Venkat Subramaniam and Andy Hunt — Another great book in the "Pragmatic" tradition. I was expecting a book with more on agile software development methods, but this is more individual things developers can do to increase their productivity. While I already do a lot of these, it's always good to be reminded of what you're naturally doing and why it works.

After The Gold Rush by Steve McConnell — Recommended for anyone interested how software development can become a true engineering discipline instead of the craft that it currently is in most of the industry.

Waltzing With Bears: Managing Risk on Software Projects by Tom DeMarco and Timothy Lister — From the authors who wrote "Peopleware. While the word "agile" isn't used in the book, a lot of the topics here strongly related to agile practices.

Bargaining for Advantage: Negotiation Strategies for Reasonable People 2nd Edition by G. Richard Shell — An excellent "soft skills" book. I've read "Getting to Yes" and "Getting Past No", and this book was an excellent complement to those two. Lots of concrete strategies for negotiation and guidance for what works in what situations.

Books, December 2008

Recommended:

Effective Java by Joshua BlochTHE book for honing your Java-fu. If you haven't read this, stop reading and go buy it now. There's so much good stuff in this book, I savored it over about three months.

Peopleware: Productive Projects and Teams by Tom DeMarco and Timothy Lister
— A classic in software development management. Much of what Joel Spolsky has used to build Fog Creek and which he writes about is derived from this book. A warning though, if you work in a cube for a large company, you will probably be tempted to quit :)

Groovy in Action by Dierk Koenig, Andrew Glover, Paul King, and Guillaume Laforge — Groovy is a fantastic language, built around the desire to be useful and productive. This is a good introduction from some of the project leads. I would like to have more content on Groovy's metaprogramming capabilities. The MarkupBuilder section of the book was very handy when I was re-implementing a mess of a Java/XSLT report generator, ending up with about half the lines of code and far greater readability.

The Productive Programmer by Neal Ford — Following in the tradition of "The Pragmatic Programmer", this book provides a bevy of ideas of how to improve your programming. The treatment of many of the subjects is relatively shallow, but everything from window launchers to code coverage is somehow hit. Ford also mentions specific software packages various tasks, which takes the great risk of quickly dating the book but makes it much more useful for the reader.

Not Recommended:

Differentiate or Die: Survival in Our Era of Killer Competition by Jack Trout and Steve Rivkin — I was hoping for a book on how to differentiate your product in the marketplace based on creating innovative products, but this is more "how to lie to people about your product when it's just like everybody else's." "It's Toasted" indeed.

Bad UI: Avery DesignPro

Avery is mainly know for their blank printer labels. Many people use Word for creating labels, but Avery also has their own program, Avery DesignPro. I used this last week to print some Christmas card labels, and found an excellent example of poor UI component design.

In these two screenshots, can you look at them and tell which means "print a full sheet of this label" and which means "print a different individual labels"? The relevant part of the UI is the All Same "button".

picture-2-21-34-47

picture-3

The answer is actually neither. The entire area of "All Same"/"On"/"Off" is one big button, rather than a label and a toggle. Clicking changes the value, rather than performing the action that the button indicates. This is very confusing to users, since it's rare that you click an off button to turn something on. A better design would to have two radio buttons with "all labels the same" and "different individual labels", or some such text, so that it would be clear which of these was currently selected and which one you were changing to.

Code coverage is like compiling

Several months ago, I began a concerted effort at work to get our code coverage numbers up. This was prompted by an upper management target of 85% code coverage by a certain date, which I initially saw as unrealistic within any timeframe. I hadn't done much work with code coverage, but I did know the primary drawback, in that most tools simply show that code was executed and not that all paths through the code were executed (branch coverage). Any simple metric has the potential to be abused by naive management, since it's easier to measure code coverage than measure if the code is actually being tested correctly and fulfills the desired usecases (assuming they even exist!).

Several months later, now at 85%, I have a more positive and specific view:

Coverage is to testing as compiling is to coding.

That is, it doesn't ensure that your testing is complete, or correct, or anything, but it does make sure that nothing is completely wrong. If code is never even executed, you have zero assurance that it is correct, just as if code that won't compile has zero assurance of being correct. That doesn't mean it is correct, just that it has a non-zero probability of being correct. The realities of software development allow you to only increase the probably of correctness, so this is one more tool to do this.

I found large blocks of code that weren't being run at all, for various reasons. There were a few methods that intended to override superclass methods (and weren't annotated with @Overrides because the code was originally 1.4 based), but had subtle name or signature typos. Some code had subtle logic errors in branches which prevented one way from executing.

Code coverage metrics were particularly useful in the case I was in, where I had inherited a large amount of complex code from another developer who hadn't provided the most thorough set of tests. I could easily see what code wasn't being executed, and then devise test cases to cover these. One has to be very careful when doing this, since you only get one chance to test that section of code correctly, since after the first test which covers the code, you no longer have the obvious warning of uncovered code. This presents the developer with a moral hazard, since they can write the simple test to get their code coverage numbers up or they can write exhaustive tests which correctly test the code and contribute to genuine code quality. You only get one chance to do the right thing.

Double dereferenced properties in Ant with Groovy

The TestNG report system is nice, but sometimes you need to integrate the TestNG output with some other test reporting system. At work, this other system requires that a single ".suc" or ".dif" file written to a common directory for each "test" you run, where "test" is defined however you want. In our case, we do one test for each TestNG group we run.

Due to Ant's immutable properties, I found it difficult to get exactly behavior I wanted. The simple way was to set a property "has.failure" if the testng-failed.xml file existed, but then would have the effect of appearing that a bunch of tests had failed, rather than a single one. It wasn't that important of an issue, since we it it was easy to see which test had actually failed afterward.

I finally got around to fixing this today. I screwed around with ant tasks for a while, but finally decided to use Groovy, which I'll choose first next time. The main issue was with the double dereference — I wanted to create a property based on the groups the test was running, and then reference it the same way, e.g., create property ${foo}.failed, and then access it like "${${foo}.failed}" (which doesn't work). Ant lets you create this property, but then requires you to jump through some as-yet-unknown-to-me hoops in order to actually reference the property. However, this is simple in Groovy, shown below.

This basically replaces the "condition" tasks in my description of calling the testng task in this post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<target name="run-testng" depends="" >
    ... call testng here
 
       <condition property="${groups}.has.failure" value="true" else="false">
          <available file="${testng.report.dir}/${groups}/testng-failed.xml"/>
       </condition>
 
       <antcall target="process-results">
          <param name="infix" value="${groups}"/>
       </antcall>
</target>
 
<target name="process-results">
       <groovy>
          def infix = properties['infix']
          def dest = properties['RESULTS_DIR']
          def suc = new File("${dest}/product.name.${infix}.suc")
          def dif = new File("${dest}/product.name.${infix}.dif")
          if (properties["${infix}.has.failure"] == "true"){
            dif.write('Pass')
            suc.delete()
          } else {
            suc.write('Pass')
            dif.delete()
          }
    </groovy>
</target>

And, yes, the framework we have requires "Pass" in the dif file– don't ask me.