closestMatch in sorted-collection: j^python^c++

–java is cleanest. P236 (P183 for Set) [[java generics]] lists four methods belonging to the NavigableMap interface

  • ceilingEntry(key) — closest entry higher or equal
  • higherEntry(key) — closest entry strictly higher than key
  • lowerEntry
  • floorEntry
Advertisements

java assignment-expression returns assigned value

A small halo in coding interviews 🙂

This feature is inherited from C.

  • eg: return singletonInstance = new MySingleton();
  • eg: a = b = 22;
  • eg: while ((line = reader.readLine()) != null)
  • eg (bigger):
    List<Object> processables;
    while ((processables = retrieveProcessableItems(..)).size() > 0) {/*/}

Incidentally, unlike C, java while-loop header can’t declare variables 😦  Solution — use for-loop 🙂

get class file physical location of a java object #home made

<![CDATA[ private static String getMetaData4Obj(Object o) { return getMetaData4Class(o.getClass()); } private static String getMetaData4Class(Class clazz) { String className = clazz.getName(); className = className.replace(“.”, “/”); className += “.class”; URL location = clazz.getClassLoader().getResource(className); return clazz + ” loaded from ” + location; } ]]>

simple diff class – in pure java, easily embeddable (src)

<![CDATA[ //Based on work by other authors import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; import java.util.Formatter; /** * This single-file self-contained utility consists of 1 public class and 2 * non-public classes (non-CamelCase names). *

* USAGE: diff oldfile newfile *

* This program assumes that “oldfile” and “newfile” are text files. The program * writes to stdout a description of the changes which would transform “oldfile” * into “newfile”. *

* The printout is in the form of commands, each followed by a block of text. * The text is delimited by the commands, which are: *

* DELETE AT n ..deleted lines *

* INSERT BEFORE n ..inserted lines *

* n MOVED TO BEFORE n ..moved lines *

* n CHANGED FROM ..old lines CHANGED TO ..newer lines *

* The line numbers all refer to the lines of the oldfile, as they are numbered * before any commands are applied. The text lines are printed as-is, without * indentation or prefixing. The commands are printed in upper case, with a * prefix of “>>>>”, so that they will stand out. Other schemes may be * preferred. *

* Files which contain more than MAXLINECOUNT lines cannot be processed. This * can be fixed by changing “symbol” to a Vector. The algorithm is taken from * Communications of the ACM, Apr78 (21, 4, 264-), * “A Technique for Isolating Differences Between Files.” Ignoring I/O, and * ignoring the symbol table, it should take O(N) time. This implementation * takes fixed space, plus O(U) space for the symbol table (where U is the * number of unique lines). Methods exist to change the fixed space to O(N) * space. *

* Note that this is not the only interesting file-difference algorithm. In * general, different algorithms draw different conclusions about the changes * that have been made to the oldfile. This algorithm is sometimes “more right”, * particularly since it does not consider a block move to be an insertion and a * (separate) deletion. However, on some files it will be “less right”. This is * a consequence of the fact that files may contain many identical lines * (particularly if they are program source). Each algorithm resolves the * ambiguity in its own way, and the resolution is never guaranteed to be * “right”. However, it is often excellent. This program is intended to be * pedagogic. Specifically, this program was the basis of the Literate * Programming column which appeared in the Communications of the ACM (CACM), in * the June 1989 issue (32, 6, 740-755). *

* By “pedagogic”, I do not mean that the program is gracefully worded, or that * it showcases language features or its algorithm. I also do not mean that it * is highly accessible to beginners, or that it is intended to be read in full, * or in a particular order. Rather, this program is an example of one * professional’s style of keeping things organized and maintainable. *

* The program would be better if the “print” variables were wrapped into a * struct. In general, grouping related variables in this way improves * documentation, and adds the ability to pass the group in argument lists. This * program is a de-engineered version of a program which uses less memory and * less time. The article points out that the “symbol” arrays can be implemented * as arrays of pointers to arrays, with dynamic allocation of the subarrays. * (In C, macros are very useful for hiding the two-level accesses.) In Java, a * Vector would be used. This allows an extremely large value for MAXLINECOUNT, * without dedicating fixed arrays. (The “other” array can be allocated after * the input phase, when the exact sizes are known.) The only slow piece of code * is the “strcmp” in the tree descent: it can be speeded up by keeping a hash * in the tree node, and only using “strcmp” when two hashes happen to be equal. * * @author Ian F. Darwin, Java version * @author D. C. Lindsay, C version (1982-1987) * @author minor adjustment by Bin Tan */ public class Diff { /* The following are global to printout’s subsidiary routines */ // enum{ idle, delete, insert, movenew, moveold, // same, change } printstatus; public static final int idle = 0, delete = 1, insert = 2, movenew = 3, moveold = 4, same = 5, change = 6; private static final String LINE = “————————–“; /** * block len > any possible real block len */ final static int UNREAL = Integer.MAX_VALUE; /** * main – entry point when used standalone. NOTE: no routines return error * codes or throw any local exceptions. Instead, any routine may complain to * stderr and then quit with error to the system. */ public static void main(String argstrings[]) { argstrings = new String[] { “C:\\0\\Copy of c#IV_question.txt”, “C:\\0\\c#IV_question.txt” }; if (argstrings.length != 2) { throw new RuntimeException(“Usage: diff oldfile newfile”); } Diff d = new Diff(); Formatter f = d.doDiff(argstrings[0], argstrings[1]); System.out.println(f); return; } boolean anyprinted; /** * blocklen is the info about found blocks. It will be set to 0, except at * the line#s where blocks start in the old file. At these places it will be * set to the # of lines in the block. During printout , this # will be * reset to -1 if the block is printed as a MOVE block (because the printout * phase will encounter the block twice, but must only print it once.) The * array declarations are to MAXLINECOUNT+2 so that we can have two extra * lines (pseudolines) at line# 0 and line# MAXLINECOUNT+1 (or less). */ int blocklen[]; final public Formatter formatter = new Formatter(new StringBuilder()); /** * Keeps track of information about file1 and file2 */ fileInfo oldinfo, newinfo; int printstatus, printoldline, printnewline; // line numbers in old & new file public Diff() { } /** * Do one file comparison. Called with both filenames. */ public Formatter doDiff(String oldFile, String newFile) { println(“>>>> Difference of file \”” + oldFile + “\” and file \”” + newFile + “\”.\n”); oldinfo = new fileInfo(oldFile); newinfo = new fileInfo(newFile); /* we don’t process until we know both files really do exist. */ try { inputscan(oldinfo); inputscan(newinfo); } catch (IOException e) { System.err.println(“Read error: ” + e); } /* * Now that we’ve read all the lines, allocate some arrays. */ blocklen = new int[(oldinfo.maxLine > newinfo.maxLine ? oldinfo.maxLine : newinfo.maxLine) + 2]; oldinfo.alloc(); newinfo.alloc(); /* Now do the work, and print the results. */ transform(); return printout(); } /** * inputscan Reads the file specified by pinfo.file. ——— Places the * lines of that file in the symbol table. Sets pinfo.maxLine to the number * of lines found. */ @SuppressWarnings(“deprecation”) void inputscan(fileInfo pinfo) throws IOException { String linebuffer; pinfo.maxLine = 0; while ((linebuffer = pinfo.file.readLine()) != null) { storeline(linebuffer, pinfo); // System.out.println(linebuffer); } } /* * newconsume Part of printout. Have run out of old file. Print the rest of * the new file, as inserts and/or moves. */ void newconsume() { for (;;) { if (printnewline > newinfo.maxLine) break; /* end of file */ if (newinfo.other[printnewline] oldinfo.maxLine) break; /* end of file */ printnewline = oldinfo.other[printoldline]; if (printnewline < 0) showdelete(); else if (blocklen[printoldline] oldinfo.maxLine) { newconsume(); break; } if (printnewline > newinfo.maxLine) { oldconsume(); break; } if (newinfo.other[printnewline] < 0) { if (oldinfo.other[printoldline] < 0) this.showchange(); else showinsert(); } else if (oldinfo.other[printoldline] < 0) showdelete(); else if (blocklen[printoldline] >>> End of differences.”); else println(“>>>> Files are identical.”); return formatter; } /* * scanafter Expects both files in symtab, and oldinfo and newinfo valid. * Expects the “other” arrays contain positive #s to indicate lines that are * unique in both files. For each such pair of places, scans past in each * file. Contiguous groups of lines that match non-uniquely are taken to be * good-enough matches, and so marked in “other”. Assumes each other[0] is * 0. */ void scanafter() { int oldline, newline; for (newline = 0; newline = 0) { /* is unique in old & new */ for (;;) { /* scan after there in both files */ if (++oldline > oldinfo.maxLine) break; if (oldinfo.other[oldline] >= 0) break; if (++newline > newinfo.maxLine) break; if (newinfo.other[newline] >= 0) break; // oldline & newline exist, and aren’t already matched if (newinfo.symbol[newline] != oldinfo.symbol[oldline]) break; // not same newinfo.other[newline] = oldline; // record a match oldinfo.other[oldline] = newline; } } } } /** * scanbefore As scanafter, except scans towards file fronts. Assumes the * off-end lines have been marked as a match. */ void scanbefore() { int oldline, newline; for (newline = newinfo.maxLine + 1; newline > 0; newline–) { oldline = newinfo.other[newline]; if (oldline >= 0) { /* unique in each */ for (;;) { if (–oldline = 0) break; if (–newline = 0) break; // oldline and newline exist, and aren’t marked yet if (newinfo.symbol[newline] != oldinfo.symbol[oldline]) break; // not same newinfo.other[newline] = oldline; // record a match oldinfo.other[oldline] = newline; } } } } /** * scanblocks – Finds the beginnings and lengths of blocks of matches. Sets * the blocklen array (see definition). Expects oldinfo valid. */ void scanblocks() { int oldline, newline; int oldfront = 0; // line# of front of a block in old, or 0 int newlast = -1; // newline’s value during prev. iteration for (oldline = 1; oldline <= oldinfo.maxLine; oldline++) blocklen[oldline] = 0; blocklen[oldinfo.maxLine + 1] = UNREAL; // starts a mythical blk for (oldline = 1; oldline <= oldinfo.maxLine; oldline++) { newline = oldinfo.other[oldline]; if (newline < 0) oldfront = 0; /* no match: not in block */ else { /* match. */ if (oldfront == 0) oldfront = oldline; if (newline != (newlast + 1)) oldfront = oldline; ++blocklen[oldfront]; } newlast = newline; } } /* * scanunique Scans for lines which are used exactly once in each file. * Expects both files in symtab, and oldinfo and newinfo valid. The * appropriate "other" array entries are set to the line# in the other file. * Claims pseudo-lines at 0 and XXXinfo.maxLine+1 are unique. */ void scanunique() { int oldline, newline; node psymbol; for (newline = 1; newline >>> ” + printoldline + ” CHANGED FROM”); printstatus = change; oldinfo.symbol[printoldline].showSymbol(); anyprinted = true; printoldline++; } /** * showdelete Part of printout. Expects printoldline is at a deletion. */ void showdelete() { if (printstatus != delete) println(“\n>>>> DELETE AT ” + printoldline); printstatus = delete; oldinfo.symbol[printoldline].showSymbol(); anyprinted = true; printoldline++; } /* * showinsert Part of printout. Expects printnewline is at an insertion. */ void showinsert() { if (printstatus == change) println(LINE + “\n>>>> CHANGED TO”); else if (printstatus != insert) println(“\n>>>> INSERT BEFORE ” + printoldline); printstatus = insert; newinfo.symbol[printnewline].showSymbol(); anyprinted = true; printnewline++; } /** * showmove Part of printout. Expects printoldline, printnewline at start of * two different blocks ( a move was done). */ void showmove() { int oldblock = blocklen[printoldline]; int newother = newinfo.other[printnewline]; int newblock = blocklen[newother]; if (newblock = newblock) { // assume new’s blk moved. blocklen[newother] = -1; // stamp block as “printed”. println(“>>>> ” + newother + ” THRU ” + (newother + newblock – 1) + ” MOVED TO BEFORE ” + printoldline); for (; newblock > 0; newblock–, printnewline++) newinfo.symbol[printnewline].showSymbol(); anyprinted = true; printstatus = idle; } else /* assume old’s block moved */ skipold(); /* target line# not known, display later */ } /** * showsame Part of printout. Expects printnewline and printoldline at start * of two blocks that aren’t to be displayed. */ void showsame() { int count; printstatus = idle; if (newinfo.other[printnewline] != printoldline) { throw new RuntimeException(“BUG IN LINE REFERENCING”); } count = blocklen[printoldline]; printoldline += count; printnewline += count; } /** * skipnew Part of printout. Expects printnewline is at start of a new block * that has already been announced as a move. Skips over the new block. */ void skipnew() { int oldline; printstatus = idle; for (;;) { if (++printnewline > newinfo.maxLine) break; /* end of file */ oldline = newinfo.other[printnewline]; if (oldline oldinfo.maxLine) break; /* end of file */ if (oldinfo.other[printoldline] fileInfo.MAXLINECOUNT) { throw new RuntimeException(“MAXLINECOUNT exceeded, must stop.”); } pinfo.symbol[linenum] = node.addSymbol(linebuffer, pinfo == oldinfo, linenum, this.formatter); } /* * transform Analyzes the file differences and leaves its findings in the * global arrays oldinfo.other, newinfo.other, and blocklen. Expects both * files in symtab. Expects valid “maxLine” and “symbol” in oldinfo and * newinfo. */ void transform() { int oldline, newline; int oldmax = oldinfo.maxLine + 2; /* Count pseudolines at */ int newmax = newinfo.maxLine + 2; /* ..front and rear of file */ for (oldline = 0; oldline < oldmax; oldline++) oldinfo.other[oldline] = -1; for (newline = 0; newline < newmax; newline++) newinfo.other[newline] = -1; scanunique(); /* scan for lines used once in both files */ scanafter(); /* scan past sure-matches for non-unique blocks */ scanbefore(); /* scan backwards from sure-matches */ scanblocks(); /* find the fronts and lengths of blocks */ } }; // end of main class! /** * This is the info kept per-file. */ class fileInfo { static final int MAXLINECOUNT = 20000; DataInputStream file; /* File handle that is open for read. */ public int maxLine; /* After input done, # lines in file. */ int other[]; /* Map of line# to line# in other file */ node symbol[]; /* The symtab handle of each line. */ /* ( -1 means don't-know ). */ /* Allocated AFTER the lines are read. */ /** * Normal constructor with one filename; file is opened and saved. */ fileInfo(String filename) { symbol = new node[MAXLINECOUNT + 2]; other = null; // allocated later! try { file = new DataInputStream(new FileInputStream(filename)); } catch (IOException e) { System.err.println("Diff can't read file " + filename); System.err.println("Error Exception was:" + e); throw new RuntimeException(e); } } // This is done late, to be same size as # lines in input file. void alloc() { other = new int[symbol.length + 2]; } } /** * Class "node". The symbol table routines in this class all understand the * symbol table format, which is a binary tree. The methods are: addSymbol, * symbolIsUnique, showSymbol. */ class node { /* the tree is made up of these nodes */ static final int freshnode = 0, oldonce = 1, newonce = 2, bothonce = 3, other = 4; static node panchor = null; /* symtab is a tree hung from this */ /** * addSymbol(String pline) – Saves line into the symbol table. Returns a * handle to the symtab entry for that unique line. If inoldfile nonzero, * then linenum is remembered. */ static node addSymbol(String pline, boolean inoldfile, int linenum, Formatter formatter) { node pnode; pnode = matchsymbol(pline, formatter); /* find the node in the tree */ if (pnode.linestate == freshnode) { pnode.linestate = inoldfile ? oldonce : newonce; } else { if ((pnode.linestate == oldonce && !inoldfile) || (pnode.linestate == newonce && inoldfile)) pnode.linestate = bothonce; else pnode.linestate = other; } if (inoldfile) pnode.linenum = linenum; return pnode; } /** * matchsymbol Searches tree for a match to the line. * * @param pline * pline, a line of text If node's linestate == freshnode, then * created the node. * @param formatter */ static node matchsymbol(String pline, Formatter formatter) { int comparison; node pnode = panchor; if (panchor == null) return panchor = new node(pline, formatter); for (;;) { comparison = pnode.line.compareTo(pline); if (comparison == 0) return pnode; /* found */ if (comparison 0) { if (pnode.pright == null) { pnode.pright = new node(pline, formatter); return pnode.pright; } pnode = pnode.pright; } } /* NOTE: There are return stmts, so control does not get here. */ } final private Formatter formatter; String line; int linenum; int /* enum linestates */linestate; node pleft, pright; /** * Construct a new symbol table node and fill in its fields. * * @param pline * A line of the text file */ node(String pline, Formatter formatter) { this.formatter = formatter; pleft = pright = null; linestate = freshnode; /* linenum field is not always valid */ line = pline; } /** * showSymbol Prints the line to stdout. */ void showSymbol() { this.formatter.format(line+”\n”); // System.out.println(line); } /** * symbolIsUnique Arg is a ptr previously returned by addSymbol. * ————– Returns true if the line was added to the symbol table * exactly once with inoldfile true, and exactly once with inoldfile false. */ boolean symbolIsUnique() { return (linestate == bothonce); } } ]]>

custom logger to show src line num, caller, parent caller and grandparent caller

<![CDATA[ /** based on http://javablog.co.uk/2008/07/12/logging-with-javautillogging/* Class name is mentioned in C:\Program Files\Java\jre1.6.0_06\lib\logging.properties */ public class JDKLoggingForamtter extends SimpleFormatter { private final String lineSeparator; public JDKLoggingForamtter(){ lineSeparator = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction(“line.separator”)); } @Override public synchronized String format(LogRecord record) { String sb = super.format(record); String lineNum = getEclipseFormat(); return sb.replaceFirst(lineSeparator, ” ” + lineNum + lineSeparator); } /** * Returns caller location information in eclipse format eg (Filename.java:23) * WARNING Generating caller location information is extremely slow. * It's use should be avoided unless execution speed is not an issue. * * @return the eclipse format */ private static String getEclipseFormat() { // getStackTrace can be expensive StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); String upstairs = “”; int lineNumber = 0; //StringBuilder sb = new StringBuilder(); // Here is an example of the typical stack trace we get back. // level 0 (Thread.java:1436) getStackTrace // level 1 (CustomFormatter.java:139) getFileLineNumber // level 2 (CustomFormatter.java:128) format // level 3 (StreamHandler.java:179) publish // level 4 (ConsoleHandler.java:88) publish // level 5 (Logger.java:458) log // level 6 (Logger.java:480) doLog // level 7 (Logger.java:503) log // level 8 (YourCodeHere.java:26) someMethod if (stackTrace.length >= 9) { String fileName = stackTrace[8].getFileName(); lineNumber = stackTrace[8].getLineNumber(); // Each of these calls back into logger.logp with the appropriate // level and message. This adds one extra level to the stack trace // logger.finest(“finest message”); // logger.finer(“finer message”); // logger.fine(“fine message”); // logger.info(“info message”); // logger.warning(“warning message”); // logger.severe(“severe message”); // logger.entering(“SomeClass”, “aMethod”); // logger.exiting(“SomeClass”, “aMethod”); // logger.exiting(“SomeClass”, “aMethod”, “returnval”); // logger.config(“config message”); // // Here is an example stack trace from a call to exiting // level 0 (Thread.java:1436) getStackTrace // level 1 (CustomFormatter.java:139) getFileLineNumber // level 2 (CustomFormatter.java:128) format // level 3 (StreamHandler.java:179) publish // level 4 (ConsoleHandler.java:88) publish // level 5 (Logger.java:458) log // level 6 (Logger.java:480) doLog // level 7 (Logger.java:623) logp // level 8 (Logger.java:938) exiting // level 9 (YourCodeHere.java:27) someMethod // // We could check the name of the method we are in and only go one level deeper // if it is one of “finest”, “finer”, “fine”, “info”, “warning”, // “severe”, “config”, “entering” or “exiting” but that would be too much trouble. // If the stack is in Logger.java and we can go one level deeper – do it. if (stackTrace.length >= 10 && “Logger.java”.equals(fileName)) { fileName = stackTrace[9].getFileName(); lineNumber = stackTrace[9].getLineNumber(); } if (stackTrace.length >= 10){ upstairs = ” = 11){ upstairs += “

Formatter.java – replacing StringBuilder

In most cases, I can replace StringBuilder with a Formatter, but of course Formatter offers more!

new Formatter(new StringBuilder()) painlessly appends!

import java.util.Formatter;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

public class FormatterTest {
    private Formatter formatter;
    private StringBuilder sb;
    @Before
    public void setUp() {
        this.formatter = new Formatter(new StringBuilder());
        this.sb = new StringBuilder();
    }
    @Test
    public void testArray() {
        char[] var= new char[] {‘a’, ‘b’, ‘c’};
        sb.append(var+””);
        formatter.format(var+””, “”);
        assertEquals(sb.toString(), formatter.toString());
    }
    @Test
    public void testInt() {
        int var=902;
        sb.append(var);
        formatter.format(var+””, “”);
        assertEquals(sb.toString(), formatter.toString());
    }
    @Test
    public void testFloat() {
        float var=902;
        sb.append(var);
        formatter.format(var+””, “”);
        assertEquals(sb.toString(), formatter.toString());
    }
}

homemade serializer/deserializer: object⇔file

public class Serializer {
	private static final String FILE = "c:\temp\"+Serializer.class.getSimpleName();

		public static void main(String[] args) throws IOException, ClassNotFoundException{
		//writeToFile(bean);
		Serializer.readFromFile();
	}
		public static void writeToFile(Object input) {
		try {
			ObjectOutputStream s = new ObjectOutputStream(new FileOutputStream(FILE));
			s.writeObject(input);
			s.flush();
		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
			throw new IllegalStateException(e);
		}
		catch (IOException e) {
			e.printStackTrace();
			throw new IllegalStateException(e);
		}
	}

	/** @param Target Type. Will cast the de-serialized object into this type.
	* @return
	* @throws ClassNotFoundException
	*/
	public static T readFromFile() throws ClassNotFoundException {
		Object o = null;
		try {
			ObjectInputStream s = new ObjectInputStream(new FileInputStream(FILE));
			o = s.readObject();
		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
			throw new IllegalStateException(e);
		}
		catch (IOException e) {
			e.printStackTrace();
			throw new IllegalStateException(e);
		}
		if (o == null) return null;
		T uncompressed = (T)o;
		out.println(uncompressed + " ");
		return uncompressed;
	}
}

calling super.method1()

Use case: C.java extends B.java. Both defines a non-static m(). In C’s instance methods including C’s m(), and C’s constructors, you can write super.m(). For the justification, consider toString(), hashCode() and equals().

 

Suppose we have an object B b = new C() and C c =new C()

 

Illegal use case: calling c.super.m() from another class.

 

Conclusion: super.m() ie the parent’s version of m() is only accessible from the child’s methods.

java naming convention — registration

For an architect and a student, it’s good know ..

when a method or field or argument is named like …register…, it often means “add a CALLBACK to an OBSERVABLE”. The observable (subject, dispatcher..) often holds a list of listeners. When some life cycle event occurs, it /notifies/ them. See other posts on callback, listener, async…

Registration is used in DI, Hollywood principal and many “frameworks”

examples:
* registerShutdownHook

how to instantiate a new java Date object

Q: how to instantiate a new java Date object with 3 numbers for year, month and date?

I guess Calendar could be one standard solution, but not a one-liner.

Practical solution for the time being: use the deprecated constructors

— new java.util.Date?

Only deprecated constructors do that.
java.sql.Date.valueOf(“2008-04-02” )

–new java.sql.Date?

Only deprecated constructors do that.
java.util.Date.getTime() can feed a long to a java.sql.Date() constructor

see major.minor versions of a classfile

http://blogs.sun.com/sundararajan/entry/thou_shall_know_the_class

http://forum.java.sun.com/thread.jspa?threadID=5178593&messageID=9692487 says : Just open the class file with something that can display hexidecimal data and look at bytes 5 – 8 in the file. They contain the minor:major versions in hex.

I used textpad and saw 2E ie 48 for major version. javap reported 0 and 0.

StringBuffer / StringBuilder offers no prepend()

Use insert(). Example —

   myStringBuffer.insert (0, “prefix string”); // C# and java are similar. See API links below

This technique is rather important to job interview coding tests. (I encountered this in the ITA 50 billion quiz)

Warning — http://stackoverflow.com/questions/738950/c-sharp-or-java-prepend-strings-with-stringbuilder points out that prepend is often less efficient as append

myBuf.insert(5, …) means “insert after 5“, that is, insert after first 5 characters of myBuf.
myBuf.insert(0, …) means insert after 0 character, which is the geeky way of saying prepend

http://docs.oracle.com/javase/6/docs/api/java/lang/StringBuffer.html
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/StringBuilder.html — faster but thread unsafe

http://msdn.microsoft.com/en-us/library/7tyt41s0(v=vs.110).aspx

log4j.xml quick guide

q: threshold?
A: The “threshold” attribute takes a level value such that all
logging statements with a level equal or below this value are
disabled.

Q: root?
A: root of the xml tree is CATEGORY, which typicall references
1 or more appenders, 2nd level node in the xml tree.

Q: eg@appenders?
A: console, snmp, syslog,

q: log4j.xml changes need restart?
A: no

Q: category vs logger?
A: say “category” in xml; say “logger” in src code
#1 tip: child logger inherits from parent logger

Q: Category names are FQCN?
A: yes. “Engine” shown in console but “com.domain.Engine” shown in log file.