net.nand.util.i18n
Class ParsedPropsFilePair

java.lang.Object
  extended by net.nand.util.i18n.ParsedPropsFilePair

public class ParsedPropsFilePair
extends java.lang.Object

Represents a source-language and destination-language pair of properties files. There should be no keys in the target that aren't in the source. Each key in the source/destination is a ParsedPropsFilePair.FileEntry here.

Remember that .properties bundle files are encoded not in UTF-8 but in ISO-8859-1:

Characters outside that encoding must use \uXXXX code escapes.

The parsed merged list is getContents(), any keys only in the destination are in getDestOnly(). The source or destination "half" can be generated with extractContentsHalf(boolean). Changes to that "half" can then be saved with PropsFileWriter.

Author:
Jeremy D Monin <jeremy@nand.net>

Nested Class Summary
static class ParsedPropsFilePair.FileCommentEntry
          Comment line in source and destination
static class ParsedPropsFilePair.FileEntry
          One message key or comment line, with its source and destination languages' string values and comments.
static class ParsedPropsFilePair.FileKeyEntry
          Key-value pair line in source and destination, with preceding comments, or multi-line comment at the end of the file.
 
Field Summary
private  java.util.List<ParsedPropsFilePair.FileEntry> cont
          Expanded entries (contents), one per line in file, from parsed: Source and dest file-pair line-by-line grid contents, from parsing and editing; also contains destOnlyPairs.
private  ParsedPropsFilePair.FileKeyEntry contEndingComment
          If the file ends with a comment not followed by a key-value pair, it goes in cont and also goes here
private  ParsedPropsFilePair.FileKeyEntry contHeadingComment
          If the file starts with a comment followed by blank lines above the first a key-value pair, comment goes in cont and also goes here
 java.io.File destFile
           
private  java.util.List<PropsFileParser.KeyPairLine> destOnlyPairs
          Key-value pairs found only in the destination file, not the source file; or null if none.
private  java.util.Map<java.lang.String,java.lang.String> destParsedDupeKeys
          Any duplicate keys found during parsing, or null if none.
private  boolean isDestNew
          If true, setDestIsNew(List) has been called.
private  java.util.List<ParsedPropsFilePair.FileKeyEntry> parsed
          Logical entries, one per key, to be expanded into cont: Source and dest file-pair key-by-key contents, from parsing; does not contain destOnlyPairs.
 java.io.File srcFile
           
private  java.util.Map<java.lang.String,java.lang.String> srcParsedDupeKeys
          Any duplicate keys found during parsing, or null if none.
 boolean unsavedDest
          If true, there are unsaved changes from editing the pair.
 boolean unsavedInsRows
          If true, there are unsaved inserted rows.
 boolean unsavedSrc
          If true, there are unsaved changes from editing the pair.
 
Constructor Summary
ParsedPropsFilePair(java.io.File src, java.io.File dest)
          Create a new empty FilePair to begin parsing.
 
Method Summary
private  void buildContFromSrcDest(java.util.Map<java.lang.String,PropsFileParser.KeyPairLine> destKeys)
          Build cont by combining the parsed source (parsed) and destination (destKeys).
 boolean convertInsertedRows()
          Check for rows added by the editor, with the ParsedPropsFilePair.FileKeyEntry.newAdd flag; inspect these for keys and values, and if needed convert them to ParsedPropsFilePair.FileCommentEntry.
private  void expandCommentLinesIntoCont(ParsedPropsFilePair.FileKeyEntry fe)
          Expand this key-value entry's preceding comment(s) to new lines appended at the end of cont.
 java.util.List<PropsFileParser.KeyPairLine> extractContentsHalf(boolean destHalf)
          Get the source or destination "half" of the parsed pair's contents, in a format suitable for PropsFileWriter.write(List, String).
 java.util.Iterator<ParsedPropsFilePair.FileEntry> getContents()
          Get the list of key-value pairs found in the source and maybe also the destination.
 java.util.Map<java.lang.String,java.lang.String> getDestDupeKeys()
          Get any keys seen during destination parsing more than once with different values.
 java.util.Iterator<PropsFileParser.KeyPairLine> getDestOnly()
          Get the list of key-value pairs found only in the destination, or null if none.
 int getDestOnlySize()
          Get the number of key-value pairs found only in the destination, or 0, in getDestOnly().
 ParsedPropsFilePair.FileEntry getRow(int r)
          Get row r of the contents.
 java.util.Map<java.lang.String,java.lang.String> getSrcDupeKeys()
          Get any keys seen during source parsing more than once with different values.
 void insertRow(int r, boolean beforeRow)
          Add/insert a row before or after an existing row.
 boolean isKeyDestOnly(java.lang.String key)
          Is this key found only in the destination file, not in the source file? This is an error and a rare occurrence.
 void parseDest()
          Parse the destination-language file at destFile; call parseSrc() before calling this method, so this method can merge the structures together into cont.
 void parseSrc()
          Parse the source-language file at srcFile.
 void setDestIsNew(java.util.List<java.lang.String> comments)
          Call this method to indicate that destFile is new and does not yet exist.
 int size()
          Get the number of key-value pairs in getContents().
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

unsavedSrc

public boolean unsavedSrc
If true, there are unsaved changes from editing the pair. If new rows were inserted, unsavedInsRows will also be set.


unsavedDest

public boolean unsavedDest
If true, there are unsaved changes from editing the pair. If new rows were inserted, unsavedInsRows will also be set.


unsavedInsRows

public boolean unsavedInsRows
If true, there are unsaved inserted rows. You can call convertInsertedRows() to make them key or comment rows. To avoid inconsistencies when saving changes, extractContentsHalf(boolean) will call that method if unsavedInsRows. If you call convertInsertedRows() yourself first, you can check its return value to see if any conversions were needed.


srcFile

public final java.io.File srcFile

destFile

public final java.io.File destFile

isDestNew

private boolean isDestNew
If true, setDestIsNew(List) has been called.


parsed

private java.util.List<ParsedPropsFilePair.FileKeyEntry> parsed
Logical entries, one per key, to be expanded into cont: Source and dest file-pair key-by-key contents, from parsing; does not contain destOnlyPairs. Built during parseSrc(), updated during parseDest().

See Also:
srcParsedDupeKeys, destParsedDupeKeys

srcParsedDupeKeys

private java.util.Map<java.lang.String,java.lang.String> srcParsedDupeKeys
Any duplicate keys found during parsing, or null if none. Key = each key seen more than once while parsing, value = values for that key. Built during parseSrc() or parseDest().

For structure details see PropsFileParser.findDuplicateKeys(List, Map), including special case of 'duplicates' with same value.

See Also:
parsed, cont

destParsedDupeKeys

private java.util.Map<java.lang.String,java.lang.String> destParsedDupeKeys
Any duplicate keys found during parsing, or null if none. Key = each key seen more than once while parsing, value = values for that key. Built during parseSrc() or parseDest().

For structure details see PropsFileParser.findDuplicateKeys(List, Map), including special case of 'duplicates' with same value.

See Also:
parsed, cont

cont

private java.util.List<ParsedPropsFilePair.FileEntry> cont
Expanded entries (contents), one per line in file, from parsed: Source and dest file-pair line-by-line grid contents, from parsing and editing; also contains destOnlyPairs. Built during parseDest() or setDestIsNew(List) by buildContFromSrcDest(Map).

See Also:
srcParsedDupeKeys, destParsedDupeKeys

contHeadingComment

private ParsedPropsFilePair.FileKeyEntry contHeadingComment
If the file starts with a comment followed by blank lines above the first a key-value pair, comment goes in cont and also goes here


contEndingComment

private ParsedPropsFilePair.FileKeyEntry contEndingComment
If the file ends with a comment not followed by a key-value pair, it goes in cont and also goes here


destOnlyPairs

private java.util.List<PropsFileParser.KeyPairLine> destOnlyPairs
Key-value pairs found only in the destination file, not the source file; or null if none. Initialized in parseDest() if needed.

Constructor Detail

ParsedPropsFilePair

public ParsedPropsFilePair(java.io.File src,
                           java.io.File dest)
Create a new empty FilePair to begin parsing.

If src and dest already exist on disk, call parseSrc() and then parseDest() to read them into this object.

Parameters:
src - Source language/locale's properties file
dest - Destination language/locale's properties file
Method Detail

size

public int size()
Get the number of key-value pairs in getContents(). Each row can be retrieved with getRow(int).


getContents

public java.util.Iterator<ParsedPropsFilePair.FileEntry> getContents()
Get the list of key-value pairs found in the source and maybe also the destination. The size of this list is size(). For access to individual rows, use getRow(int).

See Also:
extractContentsHalf(boolean), getSrcDupeKeys(), getDestDupeKeys()

extractContentsHalf

public java.util.List<PropsFileParser.KeyPairLine> extractContentsHalf(boolean destHalf)
Get the source or destination "half" of the parsed pair's contents, in a format suitable for PropsFileWriter.write(List, String).

To avoid inconsistencies when saving changes, checks unsavedInsRows and will call convertInsertedRows() if needed.

Lines with a key but no value are ignored and not extracted.

Parameters:
destHalf - True for destination half, false for source half
Returns:
List of key pair lines; may contain comment lines (null keys).
See Also:
getContents()

getRow

public ParsedPropsFilePair.FileEntry getRow(int r)
                                     throws java.lang.ArrayIndexOutOfBoundsException
Get row r of the contents. This is a reference, not a copy; if you change its fields, be sure to set the unsavedDest and/or unsavedSrc flags.

Parameters:
r - A row number within the contents, 0 <= r < size()
Returns:
The FileEntry in row r
Throws:
java.lang.ArrayIndexOutOfBoundsException

isKeyDestOnly

public boolean isKeyDestOnly(java.lang.String key)
Is this key found only in the destination file, not in the source file? This is an error and a rare occurrence.


getDestOnlySize

public int getDestOnlySize()
Get the number of key-value pairs found only in the destination, or 0, in getDestOnly().

See Also:
isKeyDestOnly(String)

getDestOnly

public java.util.Iterator<PropsFileParser.KeyPairLine> getDestOnly()
Get the list of key-value pairs found only in the destination, or null if none. The size of this list is getDestOnlySize().

See Also:
getContents(), getDestDupeKeys()

getSrcDupeKeys

public java.util.Map<java.lang.String,java.lang.String> getSrcDupeKeys()
Get any keys seen during source parsing more than once with different values. Same map format as PropsFileParser.findDuplicateKeys(List, Map).

Returns:
Duplicate keys in source file, or null if none
See Also:
getDestDupeKeys(), getContents(), getDestOnly()

getDestDupeKeys

public java.util.Map<java.lang.String,java.lang.String> getDestDupeKeys()
Get any keys seen during destination parsing more than once with different values. Same map format as PropsFileParser.findDuplicateKeys(List, Map).

Returns:
Duplicate keys in destination file, or null if none
See Also:
getSrcDupeKeys(), getContents()

parseSrc

public void parseSrc()
              throws java.lang.IllegalStateException,
                     java.io.IOException
Parse the source-language file at srcFile. After calling this method, call parseDest() or setDestIsNew(List).

Throws:
java.lang.IllegalStateException - if we've already read or created entries in this object; size() != 0
java.io.IOException - if file not found, cannot be read, etc

setDestIsNew

public void setDestIsNew(java.util.List<java.lang.String> comments)
                  throws java.lang.IllegalStateException
Call this method to indicate that destFile is new and does not yet exist. Optionally provide header comments to place in the destination.

Call parseSrc() before calling this method. If you call this method, do not call parseDest().

Parameters:
comments - any comment lines to use as the initial contents; same format as PropsFileParser.KeyPairLine.comment. Otherwise null.
Throws:
java.lang.IllegalStateException - if parseSrc() wasn't called yet, or src was empty; size() == 0

parseDest

public void parseDest()
               throws java.lang.IllegalStateException,
                      java.io.IOException
Parse the destination-language file at destFile; call parseSrc() before calling this method, so this method can merge the structures together into cont. Do not call this method if you have called setDestIsNew(List).

Merging is done using parsed, contHeadingComment, and contEndingComment. Any destination keys not found in source (in #parsed) are placed into destOnlyPairs.

Throws:
java.lang.IllegalStateException - if parseSrc() wasn't called yet, or src was empty; size() == 0; or if setDestIsNew(List) has been called
java.io.IOException - if file not found, cannot be read, etc

buildContFromSrcDest

private void buildContFromSrcDest(java.util.Map<java.lang.String,PropsFileParser.KeyPairLine> destKeys)
Build cont by combining the parsed source (parsed) and destination (destKeys). Called from parseDest() or setDestIsNew(List). Expands contHeadingComment before iterating through keys.

Because parseDest() adds entries to cont after calling this method (for destOnlyPairs), this method doesn't expand contEndingComment; the caller must do so by calling expandCommentLinesIntoCont (contEndingComment).

Parameters:
destKeys - Map from destination keys in destLines to cont entries, or null. Does not contain contHeadingComment or contEndingComment because their key would be null.

Note: destKeys contents are modified by this method:
As entries in this map are matched to source entries with the same key, each matched entry's KeyPairLine.key field will be set to null. If no source line has the same key as a given destKeys entry, at the end of the method that entry's key field will still be != null.


expandCommentLinesIntoCont

private final void expandCommentLinesIntoCont(ParsedPropsFilePair.FileKeyEntry fe)
Expand this key-value entry's preceding comment(s) to new lines appended at the end of cont.


insertRow

public void insertRow(int r,
                      boolean beforeRow)
               throws java.lang.IndexOutOfBoundsException
Add/insert a row before or after an existing row. Added rows are ParsedPropsFilePair.FileKeyEntry until convertInsertedRows() is called, they may become ParsedPropsFilePair.FileCommentEntry at that time.

Parameters:
r - Row number
beforeRow - If true, insert before (above), otherwise add after (below) this line; ignored if adding at end (r == size())
Throws:
java.lang.IndexOutOfBoundsException - if r is < 0 or > size()

convertInsertedRows

public boolean convertInsertedRows()
Check for rows added by the editor, with the ParsedPropsFilePair.FileKeyEntry.newAdd flag; inspect these for keys and values, and if needed convert them to ParsedPropsFilePair.FileCommentEntry.

Returns:
True if any rows had the flag and had their contents converted (keys, values, or comments)