class JavaPropertiesResource extends ResourceFilter {

    @Override
    public LanguageBundle parse(InputStream inStream, FilterOptions options)
            throws IOException, ResourceFilterException {

        LinkedProperties props = new LinkedProperties();
        BufferedReader inStreamReader = new BufferedReader(new InputStreamReader(inStream, getCharset()));
        String line;
        Map<String, List<String>> notesMap = new HashMap<>();
        List<String> currentNotes = new ArrayList<>();
        boolean globalNotesAvailable = true;
        List<String> globalNotes = null;
        while ((line = inStreamReader.readLine()) != null) {
            line = stripLeadingSpaces(line);
            // Comment line - Add to list of comments (notes) until we find
            // either
            // a blank line (global comment) or a key/value pair
            if (line.startsWith("#") || line.startsWith("!")) {
                // Strip off the leading comment marker, and decode Unicode escape
                // if necessary.
                if (enc == Encoding.UTF_8) {
                    // Do not unescape unicode - because if backslash u encoding is used
                    // in comment, it is on purpose and better not to decode it.
                    currentNotes.add(line.substring(1));
                } else {
                    // Unescape unicode - if not UTF-8 props, backslash u encoding must be
                    // used always.
                    currentNotes.add(unescapeOnlyUnicode(line.substring(1)));
                }
            } else if (line.isEmpty()) {
                // We are following the convention that the first blank line in
                // a properties
                // file signifies the end of a global comment.
                if (globalNotesAvailable && !currentNotes.isEmpty()) {
                    globalNotes = new ArrayList<>(currentNotes);
                    currentNotes.clear();
                } else {
                    // Just a generic blank line - treat it like a comment.
                    currentNotes.add(line);
                }
                globalNotesAvailable = false;
            } else {
                // Regular non-comment line. If there are notes outstanding that
                // apply
                // to this line, we find its key and add it to the notes map.
                StringBuffer sb = new StringBuffer(line);
                while (isContinuationLine(sb.toString())) {
                    String continuationLine = inStreamReader.readLine();
                    sb.setLength(sb.length() - 1); // Remove the continuation
                                                   // "\"
                    if (continuationLine != null) {
                        sb.append(stripLeadingSpaces(continuationLine));
                    }
                }
                String logicalLine = sb.toString();
                PropDef pd = PropDef.parseLine(logicalLine);
                String value = unescapeMessagePattern(pd.getValue(), msgPatEsc);  
                
                props.setProperty(pd.getKey(), value);
                if (!currentNotes.isEmpty()) {
                    notesMap.put(pd.getKey(), new ArrayList<>(currentNotes));
                    currentNotes.clear();
                }
            }
        }

        Iterator<Object> i = props.orderedKeys().iterator();
        LanguageBundleBuilder bb = new LanguageBundleBuilder(true);
        while (i.hasNext()) {
            String key = (String) i.next();
            List<String> notes = notesMap.get(key);
            bb.addResourceString(ResourceString.with(key, props.getProperty(key)).notes(notes));
        }
        if (globalNotes != null) {
            bb.addNotes(globalNotes);
        }
        return bb.build();
    }

    public  JavaPropertiesResource(Encoding enc, MessagePatternEscape msgPatEsc);
    public  JavaPropertiesResource();

}