alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */


package org.netbeans.modules.properties;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Position;

import org.openide.text.PositionRef;
import org.openide.text.PositionBounds;


/** 
 * Parser of .properties files. It generates structure of comment-key-vaue property elements.
 *
 * @author Petr Jiricka, Petr Hamernik, Peter Zavadsky
 * @see PropertiesStructure
 * @see Element.ItemElem
 */
class PropertiesParser {

    /** PropertiesFileEntry for which source is this parser created. */
    PropertiesFileEntry pfe;

    /** Appropriate properties editor - used for creating the PositionRefs */
    PropertiesEditorSupport editor;

    /** Properties file reader. Input stream. */
    PropertiesReader propertiesReader;
    
    /** Flag if parsing should be stopped. */
    private boolean stop = false;

    
    /** 
     * Creates parser. Has to be {@link init} afterwards.
     * @param pfe FileEntry where the properties file is stored.
     */
    public PropertiesParser(PropertiesFileEntry pfe) {
        this.pfe   = pfe;
    }

    
    /** Inits parser.
     * @exception IOException if any i/o problem occured during reading */
    void initParser() throws IOException {
        editor = pfe.getPropertiesEditor();
        propertiesReader = createReader();
    }
    
    /** Creates new input stream from the file object.
     * Finds the properties data object, checks if the document is loaded,
     * if not is loaded and created a stream from the document.
     * @exception IOException if any i/o problem occured during reading
     */
    private PropertiesReader createReader() throws IOException {
        // Get loaded document, or load it if necessary.
        Document loadDoc = null;
        
        if(editor.isDocumentLoaded()) {
            loadDoc = editor.getDocument();
        } 
    
        if(loadDoc == null) {
            loadDoc = editor.openDocument(); 
        }
            
        final Document document = loadDoc;
        final String[] str = new String[1];

        // safely take the text from the document
        document.render(new Runnable() {
            public void run() {
                try {
                    str[0] = document.getText(0, document.getLength());
                } catch(BadLocationException ble) {
                    // Should be not possible.
                    ble.printStackTrace();
                }
            }
        });

        return new PropertiesReader(str[0]);
    }

    /** Parses .properties file specified by pfe and resets its properties
     * structure.
     * @return new properties structure or null if parsing failed
     */
    public PropertiesStructure parseFile() {
        try {
            PropertiesStructure propStructure = parseFileMain();
            
            return propStructure;
        } catch(IOException e) {
            // Parsing failed, return null.
            return null;
        }
    }
    
    /** Stops parsing. */
    public void stop() {
        stop = true;
        clean();
    }
    
    /** Provides clean up after finish parsing. */
    public void clean() {
        if(propertiesReader != null) {
            try {
                propertiesReader.close();
                propertiesReader = null;
            } catch(IOException ioe) {
                org.openide.ErrorManager.getDefault().notify(org.openide.ErrorManager.INFORMATIONAL, ioe);
            }
        }
    }

    /** Parses .properties file and creates PropertiesStruture. */
    private PropertiesStructure parseFileMain() throws IOException {

        Map items = new HashMap(25, 1.0F);

        PropertiesReader reader = null;
        
        while(true) {
            if(stop) {
                // Parsing stopped -> return immediatelly.
                return null;
            }
            
            reader = propertiesReader;
            if(reader == null)
                // Parsing was stopped.
                return null;
            
            Element.ItemElem element = readNextElem(reader);
            
            if(element == null) {
                break;
            } else {
                // add at the end of the list
                items.put(element.getKey(), element);
            }
        }
        
        return new PropertiesStructure(createBiasBounds(0, reader.position), items);
    }

    /**
     * Reads next element from input stream. 
     * @return next element or null if the end of the stream occurred */
    private Element.ItemElem readNextElem(PropertiesReader in) throws IOException {
        Element.CommentElem commE;
        Element.KeyElem keyE;
        Element.ValueElem valueE;

        int begPos = in.position;

        // read the comment
        int keyPos = begPos;
        FlaggedLine fl = in.readLineExpectComment();
        StringBuffer comment = new StringBuffer();
        boolean firstNull = true;
        while (fl != null) {
            firstNull = false;
            if(fl.flag) {
                //part of the comment
                comment.append(trimComment(fl.line));
                comment.append(fl.lineSep);
                keyPos = in.position;
            } else
                // not a part of a comment
                break;
            fl = in.readLineExpectComment();
        }

        // exit completely if null is returned the very first time
        if(firstNull)
            return null;

        String comHelp;
        comHelp = comment.toString();
        if(comment.length() > 0)
            if(comment.charAt(comment.length() - 1) == '\n')
                comHelp = comment.substring(0, comment.length() - 1);

        commE = new Element.CommentElem(createBiasBounds(begPos, keyPos), UtilConvert.loadConvert(comHelp));
        // fl now contains the line after the comment or  null if none exists


        if(fl == null) {
            keyE = null;
            valueE = null;
        } else {
            // read the key and the value
            // list of
            ArrayList lines = new ArrayList(2);
            fl.startPosition = keyPos;
            fl.stringValue = fl.line.toString();
            lines.add(fl);
            int nowPos;
            while (isPartialLine(fl.line)) {
                // do something with the previous line
                fl.stringValue = fl.stringValue.substring(0, fl.stringValue/*fix: was: line*/.length() - 1);
                // now the new line
                nowPos = in.position;
                fl = in.readLineNoFrills();
                if(fl == null) break;
                // delete the leading whitespaces
                int startIndex=0;
                for(startIndex=0; startIndex < fl.line.length(); startIndex++)
                    if(UtilConvert.whiteSpaceChars.indexOf(fl.line.charAt(startIndex)) == -1)
                        break;
                fl.stringValue = fl.line.substring(startIndex, fl.line.length());
                fl.startPosition = nowPos + startIndex;
                lines.add(fl);
            }
            // now I have an ArrayList with strings representing lines and positions of the first non-whitespace character

            PositionMap positionMap = new PositionMap(lines);
            String line = positionMap.getString();

            // Find start of key
            int len = line.length();
            int keyStart;
            for(keyStart=0; keyStart 0) {
            char lead = line.charAt(0);
            if (lead == '#' || lead == '!') {
                line.deleteCharAt(0);
            } else {
                break;
            }
        }
        return line;
    }

    /** Utility method. Computes the real offset from the long value representing position in the parser.
     * @return the offset
     */
    private static int position(long p) {
        return (int)(p & 0xFFFFFFFFL);
    }

    /** Creates position bounds. For obtaining the real offsets is used
     * previous method position()
     * @param begin the begin in the internal position form
     * @param end the end in the internal position form
     * @return the bounds
     */
    private PositionBounds createBiasBounds(long begin, long end) {
        PositionRef posBegin = editor.createPositionRef(position(begin), Position.Bias.Forward);
        PositionRef posEnd = editor.createPositionRef(position(end), Position.Bias.Backward);
        return new PositionBounds(posBegin, posEnd);
    }

    /** 
     * Properties reader which allows reading from an input stream or from a string and remembers
     * its position in the document.
     */
    private static class PropertiesReader extends BufferedReader {

        /** Name constant of line separator system property. */
        private static final String LINE_SEPARATOR = "line.separator"; // NOI18N
        
        /** The character that someone peeked. */
        private int peekChar = -1;
        
        /** Position after the last character read. */
        public int position = 0;


        /** Creates PropertiesReader from buffer. */
        private PropertiesReader(String buffer) {
            super(new StringReader(buffer));
        }

        /** Creates PropertiesReader from another reader. */
        private PropertiesReader(Reader reader) {
            super(reader);
        }
        
        
        /** Read one character from the stream and increases the position.
         * @return the character or -1 if the end of the stream has been reached
         */
        public int read() throws IOException {
            int character = peek();
            peekChar = -1;
            if(character != -1)
                position++;

            return character;
        }

        /** Returns the next character without increasing the position. Subsequent calls
         * to peek() and read() will return the same character.
         * @return the character or -1 if the end of the stream has been reached
         */
        private int peek() throws IOException {
            if(peekChar == -1)
                peekChar = super.read();
            
            return peekChar;
        }

        /** Reads the next line and returns the flag as true if the line is a comment line.
         *  If the input is empty returns null
         *  Flag in the result is true if the line is a comment line
         */
        public FlaggedLine readLineExpectComment() throws IOException {
            int charRead = read();
            if(charRead == -1)
                // end of the reader reached
                return null;

            boolean decided = false;
            FlaggedLine fl = new FlaggedLine();
            while (charRead != -1 && charRead != (int)'\n' && charRead != (int)'\r') {
                if(!decided)
                    if(UtilConvert.whiteSpaceChars.indexOf((char)charRead) == -1) {
                        // not a whitespace - decide now
                        fl.flag = (((char)charRead == '!') || ((char)charRead == '#'));
                        decided = true;
                    }
                fl.line.append((char)charRead);
                charRead = read();
            }

            if(!decided)
                // all were whitespaces
                fl.flag = true;

            // set the line separator
            if(charRead == (int)'\r')
                if(peek() == (int)'\n') {
                    charRead = read();
                    fl.lineSep = "\r\n"; // NOI18N
                } else
                    fl.lineSep = "\r"; // NOI18N
            else
                if(charRead == (int)'\n') 
                    fl.lineSep = "\n"; // NOI18N
                else
                    fl.lineSep = System.getProperty(LINE_SEPARATOR);

            return fl;
        }

        /** Reads the next line. 
         * @return FlaggedLine or null if the input is empty */
        public FlaggedLine readLineNoFrills() throws IOException {
            int charRead = read();
            if(charRead == -1)
                // end of the reader reached
                return null;

            FlaggedLine fl = new FlaggedLine();
            while (charRead != -1 && charRead != (int)'\n' && charRead != (int)'\r') {
                fl.line.append((char)charRead);
                charRead = read();
            }

            // set the line separator
            if(charRead == (int)'\r')
                if(peek() == (int)'\n') {
                    charRead = read();
                    fl.lineSep = "\r\n"; // NOI18N
                } else
                    fl.lineSep = "\r"; // NOI18N
            else
                if(charRead == (int)'\n') // NOI18N
                    fl.lineSep = "\n"; // NOI18N
                else
                    fl.lineSep = System.getProperty(LINE_SEPARATOR);

            return fl;
        }

    } // End of nested class PropertiesReader.

    /**
     * Returns true if the given line is a line that must
     * be appended to the next line
     */
    private static boolean isPartialLine (StringBuffer line) {
        int slashCount = 0;
        int index = line.length() - 1;
        while((index >= 0) && (line.charAt(index--) == '\\'))
            slashCount++;
        return (slashCount % 2 == 1);
    }

    /** Nested class which maps positions in a string to positions in the underlying file.
     * @see FlaggedLine */
    private static class PositionMap {

        /** List of FlaggedLine's. */
        private ArrayList list;
        
        
        /** Constructor - expects a list of FlaggedLine */
        PositionMap(ArrayList lines) {
            list = lines;
        }
        

        /** Returns the string represented by the object */
        public String getString() {
            String allLines = ((FlaggedLine)list.get(0)).stringValue;
            for (int part=1; part= posString)
                        break;
                    else;
                else
                    if(lengthSoFar > posString)
                        break;
            }
            if(posString > lengthSoFar)
                throw new ArrayIndexOutOfBoundsException("not in scope"); // NOI18N

            return ((FlaggedLine)list.get(part)).startPosition + posString - lastLengthSoFar;
        }
    } // End of nested class PositionMap.

    
    /** Helper nested class. */
    private static class FlaggedLine {

        /** Line buffer. */
        StringBuffer line;
        
        /** Flag. */
        boolean flag;
        
        /** Line separator. */
        String lineSep;
        
        /** Start position. */
        int startPosition;
        
        /** Value. */
        String stringValue;

        
        /** Constructor. */
        FlaggedLine() {
            line = new StringBuffer();
            flag = false;
            lineSep = "\n"; // NOI18N
            startPosition = 0;
        }
    } // End of nested class FlaggedLine.
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.