Logo Search packages:      
Sourcecode: pauker version File versions  Download package

Pauker.java

/*
 * Pauker.java
 *
 * Created on 5. Juni 2001, 22:19
 */

package pauker.program;

import java.awt.Color;
import java.awt.ComponentOrientation;
import java.awt.Font;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
import tools.CsvParser;

/** The base class of Pauker which contains the GUI-independend functionality
 * needed for all Pauker versions.
 * @author Ronny.Standtke@gmx.net
 */
00046 public final class Pauker {

    /**
     * the lesson format used in Pauker v1.6.x
     */
00051     public static final String LESSON_FORMAT_V16 = "1.6";
    /**
     * the lesson format used in Pauker v1.7.x
     */
00055     public static final String LESSON_FORMAT_V17 = "1.7";

    /**
     * repeating strategies
     */
00060     public enum RepeatingStrategy {

        /**
         * the repeating strategy where the longest expired cards are repeated
         * first
         */
        OLDEST_FIRST,
        /**
         * the repeating strategy where the shortest expired cards are repeated
         * first
         */
        NEWEST_FIRST,
        /**
         * the repeating strategy where cards are repeated randomly
         */
        RANDOM_ORDER
    }

    /**
     * putback strategies
     */
00081     public enum PutbackStrategy {

        /**
         * a putback strategy where forgotten cards are put on top of the
         * unlearned batch
         */
        ON_TOP,
        /**
         * a putback strategy where forgotten cards are put at the bottom of the
         * unlearned batch
         */
        AT_BOTTOM,
        /**
         * a putback strategy where forgotten cards are put at a random place
         * within the unlearned batch
         */
        ANYWHERE
    }

    /**
     * the learning phase we are currently in
     */
00103     public enum LearningPhase {

        /**
         * not learning
         */
        NOTHING,
        /**
         * the phase of filling the ultra short term memory
         */
        FILLING_USTM,
        /**
         * the phase of waiting for the ultra short term memory
         */
        WAITING_FOR_USTM,
        /**
         * the phase of repeating the ultra short term memory
         */
        REPEATING_USTM,
        /**
         * the phase of waiting for the short term memory
         */
        WAITING_FOR_STM,
        /**
         * the phase of repeating the short term memory
         */
        REPEATING_STM,
        /**
         * the phase of repeating the long term memory
         */
        REPEATING_LTM
    }
    private static LearningPhase learningPhase = LearningPhase.NOTHING;
    /*  learning time in seconds
     *  - USTM = Ultra Short Term Memory
     *  - STM = Short Term Memory
     */
    /** the duration of the ultra short term memory */
00140     public static final int USTM_TIME = 18;
    /** the duration of the short term memory */
00142     public static final int STM_TIME = 720;
    //private static final int STM_TIME = 50;
    private static final String lineSeparator =
            System.getProperty("line.separator");
    private static final SimpleDateFormat unifiedDateFormat =
            new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
    private static final Logger logger =
            Logger.getLogger(Pauker.class.getName());
    /**
     * the major version of the current JVM
     */
00153     public final static int MAJOR_JAVA_VERSION;
    /**
     * the minor version of the current JVM
     */
00157     public final static int MINOR_JAVA_VERSION;


    static {
        String javaVersion = System.getProperty("java.version");
        String[] tokens = javaVersion.split("\\.");
        int tokenCount = tokens.length;
        if (tokenCount >= 2) {
            String majorJavaVersionString = tokens[0];
            String minorJavaVersionString = tokens[1];
            MAJOR_JAVA_VERSION = Integer.parseInt(majorJavaVersionString);
            MINOR_JAVA_VERSION = Integer.parseInt(minorJavaVersionString);
        } else {
            System.out.println("WARNING: Could not parse Java Version \"" +
                    javaVersion + "\"");
            MAJOR_JAVA_VERSION = -1;
            MINOR_JAVA_VERSION = -1;
        }
    }
    // singleton

    private Pauker() {
    }

    /**
     * returns the current learning phase of Pauker
     * @return the current learning phase of Pauker
     */
00185     public static LearningPhase getLearningPhase() {
        return learningPhase;
    }

    /**
     * sets the current learning phase of Pauker
     * @param learningPhase the current learning phase of Pauker
     */
00193     public static void setLearningPhase(LearningPhase learningPhase) {
        Pauker.learningPhase = learningPhase;
    }

    /**
     * opens a lesson (tries first to parse XML and then CSV)
     * @param path the path of the file to open
     * @param encoding the encoding to use in case of CSV
     * @throws ParserConfigurationException if the XML parser has a
     * configuration error
     * @throws IOException if an I/O exception occurs
     * @throws SAXException if the SAX parser throws an exception
     * @return the lesson parsed from the file
     */
00207     public static Lesson openLesson(String path, String encoding) throws
            ParserConfigurationException, IOException, SAXException {
        Lesson lesson = null;
        String lowerCasePath = path.toLowerCase();
        if (lowerCasePath.endsWith(".xml.gz") ||
                lowerCasePath.endsWith(".pau.gz") ||
                lowerCasePath.endsWith(".xml") ||
                lowerCasePath.endsWith(".pau")) {
            logger.finest("try first XML, then CSV");
            try {
                lesson = readXMLFile(path);
            } catch (Exception ex) {
                logger.log(Level.SEVERE, "could not parse XML file", ex);
                lesson = readCSVFile(path, encoding);
            }
        } else {
            logger.finest("try first CVS, then XML");
            try {
                lesson = readCSVFile(path, encoding);
            } catch (Exception ex) {
                logger.log(Level.SEVERE, "could not parse CSV file", ex);
                lesson = readXMLFile(path);
            }
        }
        return lesson;
    }

    /**
     * returns an XML presentation of the lesson
     * @param lesson the lesson
     * @return the XML presentation of the lesson
     * @throws ParserConfigurationException if the internal documentBuilder can
     * not be created
     * @throws TransformerException if transformation to XML fails
     * @throws UnsupportedEncodingException if UTF-8 is not supported
     */
00243     public static String lessonToXML(Lesson lesson)
            throws ParserConfigurationException, TransformerException,
            UnsupportedEncodingException {

        // create document
        DocumentBuilderFactory documentBuilderFactory =
                DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder =
                documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.newDocument();

        // fill document
        document.appendChild(document.createComment("This is a lesson file " +
                "for Pauker (http://pauker.sourceforge.net)"));
        Element lessonElement = document.createElement("Lesson");
        lessonElement.setAttribute("LessonFormat", LESSON_FORMAT_V17);
        document.appendChild(lessonElement);
        Element comment = document.createElement("Description");
        lessonElement.appendChild(comment);
        comment.appendChild(document.createTextNode(lesson.getDescription()));

        // add unlearned cards
        Element unlearnedBatchElement = addBatch(document, lessonElement);
        Batch unlearnedBatch = lesson.getUnlearnedBatch();
        for (Card card : unlearnedBatch.getCards()) {
            addCard(document, unlearnedBatchElement, card);
        }

        // add USTM and STM batch (this is stupid!!!)
        addBatch(document, lessonElement);
        addBatch(document, lessonElement);

        // add all long term batches
        for (LongTermBatch longTermBatch : lesson.getLongTermBatches()) {
            Element batchElement = addBatch(document, lessonElement);
            for (Card card : longTermBatch.getCards()) {
                addCard(document, batchElement, card);
            }
        }

        // transform document
        TransformerFactory transformerFactory =
                TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        // Note: The following property is broken in Java-1.5
        // It works with Java-1.4.x and Java-1.6
        transformer.setOutputProperty(
                "{http://xml.apache.org/xslt}indent-amount", "2");

        DOMSource source = new DOMSource(document);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        StreamResult result = new StreamResult(outputStream);
        transformer.transform(source, result);

        return outputStream.toString("UTF-8");
    }

    /**
     * returns a CSV presentation of the lesson
     * @param lesson the lesson
     * @return a CSV presentation of the lesson
     */
00307     public static String lessonToCSV(Lesson lesson) {
        StringBuilder csvString = new StringBuilder();
        batchToCSV(lesson.getUnlearnedBatch(), csvString);
        for (LongTermBatch longTermBatch : lesson.getLongTermBatches()) {
            batchToCSV(longTermBatch, csvString);
        }
        return csvString.toString();
    }

    private static Lesson readXMLFile(String path) throws
            ParserConfigurationException, SAXException, IOException {
        // parse document
        DocumentBuilderFactory documentBuilderFactory =
                DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setIgnoringComments(true);
        documentBuilderFactory.setIgnoringElementContentWhitespace(true);

        DocumentBuilder documentBuilder =
                documentBuilderFactory.newDocumentBuilder();

        InputStream inputStream = null;
        Document document = null;
        try {
            if (path.endsWith(".gz")) {
                logger.finest("reading compressed file");
                inputStream = new GZIPInputStream(new FileInputStream(path));
            } else {
                logger.finest("reading non-compressed file");
                inputStream = new FileInputStream(path);
            }
            document = documentBuilder.parse(inputStream);
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
        NodeList documentNodes = document.getChildNodes();

        Lesson newLesson = new Lesson();
        for (int i = 0, length = documentNodes.getLength(); i < length; i++) {
            Node documentNode = documentNodes.item(i);
            if ((documentNode.getNodeType() == Node.ELEMENT_NODE) &&
                    (documentNode.getNodeName().equals("Lesson"))) {
                NamedNodeMap lessonAttributes = documentNode.getAttributes();
                Node lessonFormatAttribute =
                        lessonAttributes.getNamedItem("LessonFormat");
                // old format has no LessonFormat -> null
                if (lessonFormatAttribute == null) {
                    // parse old lesson format without specified LessonFormat
                    logger.finest("no lesson format info found");
                    parseLessonv0(documentNode, newLesson);
                } else {
                    String version = lessonFormatAttribute.getNodeValue();
                    logger.finest("lesson format = \"" + version + "\"");
                    if (version.equals(LESSON_FORMAT_V16) ||
                            version.equals(LESSON_FORMAT_V17)) {
                        // parse lesson format v1.6 or v1.7 (in 1.7
                        // LearnedTimestamp is in MilliSeconds)
                        parseLessonv1_67(documentNode, newLesson, version);
                    } else {
                        // parse old lesson format (e.g. v1.4)
                        parseLessonv0(documentNode, newLesson);
                    }
                }
            }
        }

        newLesson.trim();
        return newLesson;
    }

    private static Lesson readCSVFile(String fileName, String encoding)
            throws IOException {
        FileInputStream fileInputStream = null;
        InputStreamReader reader = null;
        List<List<String>> csvFile = null;
        try {
            fileInputStream = new FileInputStream(fileName);
            if (encoding == null) {
                reader = new InputStreamReader(fileInputStream);
            } else {
                reader = new InputStreamReader(fileInputStream, encoding);
            }
            csvFile = CsvParser.parseCsvFile(reader);
        } finally {
            try {
                fileInputStream.close();
            } finally {
                reader.close();
            }
        }
        Lesson newLesson = new Lesson();
        Batch unlearnedBatch = newLesson.getUnlearnedBatch();
        SummaryBatch summaryBatch = newLesson.getSummaryBatch();
        for (List<String> csvRow : csvFile) {
            CardSide frontSide = getCsvCardSide(csvRow, 0);
            CardSide reverseSide = getCsvCardSide(csvRow, 1);
            Card card = new Card(frontSide, reverseSide);
            summaryBatch.addCard(card);
            unlearnedBatch.addCard(card);
        }

        return newLesson;
    }

    private static CardSide getCsvCardSide(List<String> csvRow, int index) {
        if (csvRow.size() > index) {
            String string = csvRow.get(index);
            if (string != null) {
                return new CardSide(string);
            }
        }
        return new CardSide("");
    }

    private static void batchToCSV(Batch batch, StringBuilder stringBuilder) {
        for (int i = 0, numberOfCards = batch.getNumberOfCards();
                i < numberOfCards; i++) {
            Card card = batch.getCard(i);
            String frontText = card.getFrontSide().getText();
            escapeCSVString(stringBuilder, frontText);
            stringBuilder.append(',');
            String reverseText = card.getReverseSide().getText();
            escapeCSVString(stringBuilder, reverseText);
            if (i != (numberOfCards - 1)) {
                stringBuilder.append(lineSeparator);
            }
        }
    }

    private static boolean mustQuote(String string) {
        return ((string.indexOf(' ') != -1) ||
                (string.indexOf('"') != -1) ||
                (string.indexOf(',') != -1) ||
                (string.indexOf('\n') != -1));
    }

    private static void escapeCSVString(
            StringBuilder stringBuilder, String string) {
        boolean mustQuote = mustQuote(string);

        if (mustQuote) {
            stringBuilder.append('"');
        }

        // we must escape all quote characters with another quote character
        // " -> ""
        for (int i = 0, length = string.length(); i < length; i++) {
            char currentChar = string.charAt(i);
            if (currentChar == '"') {
                // escape the quote character
                stringBuilder.append('"');
                stringBuilder.append('"');
            } else {
                stringBuilder.append(currentChar);
            }
        }

        if (mustQuote) {
            stringBuilder.append('"');
        }
    }

    private static void parseLessonv0(Node documentNode, Lesson newLesson) {
        Batch summaryBatch = newLesson.getSummaryBatch();
        int newLessonBatchIndex = 0;
        NodeList lessonNodes = documentNode.getChildNodes();
        for (int i = 0, lessonLength = lessonNodes.getLength();
                i < lessonLength; i++) {
            Node lessonNode = lessonNodes.item(i);
            String lessonNodeName = lessonNode.getNodeName();
            if ("Description".equals(lessonNodeName)) {
                Node descriptionNode = lessonNode.getFirstChild();
                if (descriptionNode != null) {
                    String description = descriptionNode.getNodeValue();
                    if (description != null) {
                        newLesson.setDescription(description);
                    }
                }
            } else if ("Batch".equals(lessonNodeName)) {
                if (newLesson.getNumberOfLongTermBatches() <
                        newLessonBatchIndex - 2) {
                    newLesson.addLongTermBatch();
                }
                NodeList batchNodes = lessonNode.getChildNodes();
                for (int j = 0, batchLength = batchNodes.getLength();
                        j < batchLength; j++) {
                    Node batchNode = batchNodes.item(j);
                    String batchNodeName = batchNode.getNodeName();
                    if ("Card".equals(batchNodeName)) {
                        CardSide frontSide = new CardSide();
                        CardSide reverseSide = new CardSide();
                        Card newCard = new Card(frontSide, reverseSide);
                        NamedNodeMap attributes = batchNode.getAttributes();
                        Node attribute =
                                attributes.getNamedItem("RepeatByTyping");
                        if (attribute != null &&
                                attribute.getNodeValue().equals("true")) {
                            frontSide.setRepeatByTyping(true);
                        }
                        attribute = attributes.getNamedItem(
                                "Flipped_RepeatByTyping");
                        if (attribute != null &&
                                attribute.getNodeValue().equals("true")) {
                            reverseSide.setRepeatByTyping(true);
                        }
                        NodeList cardNodes = batchNode.getChildNodes();
                        for (int l = 0, cardLength = cardNodes.getLength();
                                l < cardLength; l++) {
                            Node cardNode = cardNodes.item(l);
                            String cardNodeName = cardNode.getNodeName();
                            if ("Learned_Date".equals(cardNodeName)) {
                                Node textNode = cardNode.getFirstChild();
                                if (textNode != null) {
                                    String learnedDate =
                                            textNode.getNodeValue();
                                    try {
                                        Date learnedTimeStamp =
                                                unifiedDateFormat.parse(
                                                learnedDate);
                                        frontSide.setLearnedTimestamp(
                                                learnedTimeStamp.getTime());
                                    } catch (Exception e) {
                                        logger.log(Level.SEVERE,
                                                "could not parse date \"" +
                                                learnedDate + "\"", e);
                                    }
                                }
                            } else if ("Flipped_Learned_Date".equals(
                                    cardNodeName)) {
                                Node textNode = cardNode.getFirstChild();
                                if (textNode != null) {
                                    String learnedDate =
                                            textNode.getNodeValue();
                                    try {
                                        Date learnedTimeStamp =
                                                unifiedDateFormat.parse(
                                                learnedDate);
                                        reverseSide.setLearnedTimestamp(
                                                learnedTimeStamp.getTime());
                                    } catch (Exception e) {
                                        logger.log(Level.SEVERE,
                                                "could not parse date \"" +
                                                learnedDate + "\"", e);
                                    }
                                }
                            } else if ("FrontSide".equals(cardNodeName)) {
                                Node textNode = cardNode.getFirstChild();
                                if (textNode != null) {
                                    frontSide.setText(textNode.getNodeValue());
                                }
                            } else if ("ReverseSide".equals(cardNodeName) ||
                                    "BackSide".equals(cardNodeName)) {
                                // "BackSide" is bad English and deprecated :-)
                                Node textNode = cardNode.getFirstChild();
                                if (textNode != null) {
                                    reverseSide.setText(
                                            textNode.getNodeValue());
                                }
                            } else if ("Flipped_Batch_Number".equals(
                                    cardNodeName)) {
                                Node textNode = cardNode.getFirstChild();
                                if (textNode != null) {
                                    try {
                                        reverseSide.setLongTermBatchNumber(
                                                Integer.parseInt(
                                                textNode.getNodeValue()));
                                    } catch (Exception e) {
                                        newCard.setLearned(false);
                                    }
                                }
                            } else if ("FrontSideFont".equals(cardNodeName)) {
                                readFont(frontSide, cardNode);
                            } else if ("ReverseSideFont".equals(cardNodeName) ||
                                    "BackSideFont".equals(cardNodeName)) {
                                // "BackSide" is bad English and deprecated :-)
                                readFont(reverseSide, cardNode);
                            }
                        }
                        Batch batch = null;
                        if (newCard.isLearned()) {
                            batch = newLesson.getLongTermBatch(
                                    newLessonBatchIndex - 3);
                        } else {
                            batch = newLesson.getUnlearnedBatch();
                        }

                        batch.addCard(newCard);
                        summaryBatch.addCard(newCard);
                    }
                }
                newLessonBatchIndex++;
            }
        }
    }

    private static void parseLessonv1_67(
            Node lessonNode, Lesson newLesson, final String version) {
        Batch summaryBatch = newLesson.getSummaryBatch();
        int newLessonBatchIndex = 0;
        NodeList lessonChildNodes = lessonNode.getChildNodes();
        for (int i = 0, lessonLength = lessonChildNodes.getLength();
                i < lessonLength; i++) {
            Node lessonChildNode = lessonChildNodes.item(i);
            String lessonChildNodeName = lessonChildNode.getNodeName();
            if ("Description".equals(lessonChildNodeName)) {
                Node descriptionNode = lessonChildNode.getFirstChild();
                if (descriptionNode != null) {
                    String description = descriptionNode.getNodeValue();
                    if (description != null) {
                        newLesson.setDescription(description);
                    }
                }
            } else if ("Batch".equals(lessonChildNodeName)) {
                if (newLesson.getNumberOfLongTermBatches() <
                        (newLessonBatchIndex - 2)) {
                    newLesson.addLongTermBatch();
                }
                NodeList cardNodes = lessonChildNode.getChildNodes();
                for (int j = 0, cardLength = cardNodes.getLength();
                        j < cardLength; j++) {
                    Node cardNode = cardNodes.item(j);
                    String cardNodeName = cardNode.getNodeName();
                    if ("Card".equals(cardNodeName)) {
                        CardSide frontSide = new CardSide();
                        CardSide reverseSide = new CardSide();
                        Card newCard = new Card(frontSide, reverseSide);
                        NodeList cardSideNodes = cardNode.getChildNodes();
                        for (int k = 0, cardSideLength =
                                cardSideNodes.getLength();
                                k < cardSideLength; k++) {
                            Node cardSideNode = cardSideNodes.item(k);
                            String cardSideNodeName =
                                    cardSideNode.getNodeName();
                            if ("FrontSide".equals(cardSideNodeName)) {
                                parseCardSidev1_67(
                                        cardSideNode, frontSide, version);
                            } else if ("ReverseSide".equals(cardSideNodeName)) {
                                parseCardSidev1_67(
                                        cardSideNode, reverseSide, version);
                            }
                        }
                        // double-check that only cards in long term batches are
                        // set to be "learned"
                        if (newCard.isLearned() && newLessonBatchIndex < 3) {
                            newCard.setLearned(false);
                        }

                        Batch batch = null;
                        if (newCard.isLearned()) {
                            // must put the card into the corresponding long
                            // term batch
                            batch = newLesson.getLongTermBatch(
                                    newLessonBatchIndex - 3);
                        } else {
                            // must put the card into the unlearned batch
                            batch = newLesson.getUnlearnedBatch();
                        }
                        batch.addCard(newCard);
                        summaryBatch.addCard(newCard);
                    }
                }
                newLessonBatchIndex++;
            }
        }
    }

    private static void parseCardSidev1_67(
            Node cardSideNode, CardSide cardSide, final String version) {

        NamedNodeMap cardSideAttributes = cardSideNode.getAttributes();

        Node orientationAttribute =
                cardSideAttributes.getNamedItem("Orientation");
        if (orientationAttribute != null &&
                orientationAttribute.getNodeValue().equals("RTL")) {
            cardSide.setOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        }

        Node repeatByTypingAttribute =
                cardSideAttributes.getNamedItem("RepeatByTyping");
        if (repeatByTypingAttribute != null &&
                repeatByTypingAttribute.getNodeValue().equals("true")) {
            cardSide.setRepeatByTyping(true);
        }

        Node learnedTimestampAttribute =
                cardSideAttributes.getNamedItem("LearnedTimestamp");
        if (learnedTimestampAttribute != null) {
            cardSide.setLearned(true);
            String learnedTimestamp = learnedTimestampAttribute.getNodeValue();
            if (version.equals(LESSON_FORMAT_V16)) {
                try {
                    Date learnedDate =
                            unifiedDateFormat.parse(learnedTimestamp);
                    cardSide.setLearnedTimestamp(learnedDate.getTime());
                } catch (Exception e) {
                    logger.log(Level.SEVERE, "could not parse date \"" +
                            learnedTimestamp + "\"", e);
                }

            } else if (version.equals(LESSON_FORMAT_V17)) {
                cardSide.setLearnedTimestamp(Long.parseLong(learnedTimestamp));

            } else {
                throw new IllegalArgumentException(
                        "LessonFormat is not supported by this method!");
            }
        }

        Node batchAttribute = cardSideAttributes.getNamedItem("Batch");
        if (batchAttribute != null) {
            String batchString = batchAttribute.getNodeValue();
            try {
                int batchNumber = Integer.parseInt(batchString);
                cardSide.setLongTermBatchNumber(batchNumber);
            } catch (Exception e) {
                logger.log(Level.SEVERE, "could not parse batch number \"" +
                        batchString + "\"", e);
            }
        }

        NodeList cardSideNodes = cardSideNode.getChildNodes();
        for (int i = 0, cardSideLegth = cardSideNodes.getLength();
                i < cardSideLegth; i++) {
            Node subNode = cardSideNodes.item(i);
            String subNodeName = subNode.getNodeName();
            if ("Text".equals(subNodeName)) {
                Node textNode = subNode.getFirstChild();
                if (textNode != null) {
                    String text = textNode.getNodeValue();
                    cardSide.setText(text);
                }
            } else if ("Font".equals(subNodeName)) {
                readFont(cardSide, subNode);
            }
        }
    }

    private static void readFont(CardSide cardSide, Node node) {
        String family = null;
        int style = Font.PLAIN;
        int size = 12;
        Color fontColor = null;
        Color backgroundColor = null;

        NamedNodeMap attributes = node.getAttributes();
        Node attribute = attributes.getNamedItem("Family");
        if (attribute != null) {
            family = attribute.getNodeValue();
            attribute = attributes.getNamedItem("Size");
            if (attribute != null) {
                try {
                    size = Integer.parseInt(attribute.getNodeValue());
                } catch (NumberFormatException numberFormatException) {
                    logger.log(Level.SEVERE, "can not parse size",
                            numberFormatException);
                }
            }
            attribute = attributes.getNamedItem("Bold");
            if (attribute != null && attribute.getNodeValue().equals("true")) {
                style = Font.BOLD;
            }
            attribute = attributes.getNamedItem("Italic");
            if (attribute != null && attribute.getNodeValue().equals("true")) {
                style += Font.ITALIC;
            }
            Font font = new Font(family, style, size);

            attribute = attributes.getNamedItem("Foreground");
            if (attribute != null) {
                try {
                    int rgbValue = Integer.parseInt(attribute.getNodeValue());
                    fontColor = new Color(rgbValue);
                } catch (NumberFormatException numberFormatException) {
                    logger.log(Level.SEVERE, "can not parse foreground value",
                            numberFormatException);
                }
            }
            attribute = attributes.getNamedItem("Background");
            if (attribute != null) {
                try {
                    int rgbValue = Integer.parseInt(attribute.getNodeValue());
                    backgroundColor = new Color(rgbValue);
                } catch (NumberFormatException numberFormatException) {
                    logger.log(Level.SEVERE, "can not parse background value",
                            numberFormatException);
                }
            }

            cardSide.setFont(font);
            cardSide.setForegroundColor(fontColor);
            cardSide.setBackgroundColor(backgroundColor);
        }
    }

    private static Element addBatch(Document document, Element lesson) {
        Element batch = document.createElement("Batch");
        lesson.appendChild(batch);
        return batch;
    }

    private static void addCard(Document document, Element batch, Card card) {
        Element cardElement = document.createElement("Card");
        batch.appendChild(cardElement);

        Element frontSideElement = document.createElement("FrontSide");
        addCardSide(document, frontSideElement, card.getFrontSide(), false);
        cardElement.appendChild(frontSideElement);

        Element reverseSideElement = document.createElement("ReverseSide");
        addCardSide(document, reverseSideElement, card.getReverseSide(), true);
        cardElement.appendChild(reverseSideElement);
    }

    private static void addCardSide(Document document, Element cardSideElement,
            CardSide cardSide, boolean reverseSide) {
        // text
        Element textElement = document.createElement("Text");
        String cardSideText = filter(cardSide.getText());
        Text text = document.createTextNode(cardSideText);
        textElement.appendChild(text);
        cardSideElement.appendChild(textElement);

        // layout
        Font font = cardSide.getFont();
        if (font != null) {
            Element fontElement = document.createElement("Font");
            fontElement.setAttribute("Family", font.getFamily());
            fontElement.setAttribute("Size", String.valueOf(font.getSize()));
            fontElement.setAttribute("Bold", font.isBold() ? "true" : "false");
            fontElement.setAttribute("Italic",
                    font.isItalic() ? "true" : "false");
            Color foregroundColor = cardSide.getForegroundColor();
            if (foregroundColor != null) {
                fontElement.setAttribute("Foreground",
                        String.valueOf(foregroundColor.getRGB()));
            }
            Color backgroundColor = cardSide.getBackgroundColor();
            if (backgroundColor != null) {
                fontElement.setAttribute("Background",
                        String.valueOf(backgroundColor.getRGB()));
            }
            cardSideElement.appendChild(fontElement);
        }
        ComponentOrientation componentOrientation = cardSide.getOrientation();
        String orientationString = "RTL";
        if (componentOrientation == null ||
                componentOrientation.isLeftToRight()) {
            orientationString = "LTR";
        }
        cardSideElement.setAttribute("Orientation", orientationString);

        // learning state
        if (cardSide.isLearned()) {
            cardSideElement.setAttribute("LearnedTimestamp",
                    String.valueOf(cardSide.getLearnedTimestamp()));
            if (reverseSide) {
                // The front side does not need to set the batch attribute 
                // because it is implicitly the batch that contains the card.
                // But we must save this information for the reverse side so
                // that we can later flip the card sides.
                cardSideElement.setAttribute("Batch",
                        String.valueOf(cardSide.getLongTermBatchNumber()));
            }
        }
        boolean repeatedByTyping = cardSide.isRepeatedByTyping();
        cardSideElement.setAttribute("RepeatByTyping",
                repeatedByTyping ? "true" : "false");
    }

    /**
     * This method filters a string so that it only contains characters that are
     * within the character range of unicode:
     * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
     * @param string the string to be filtered
     * @return the filtered string
     */
00885     public static String filter(String string) {
        char[] filtered = new char[string.length()];
        int index = 0;
        for (int i = 0; i < string.length(); i++) {
            char character = string.charAt(i);
            if ((character == '\u0009') ||
                    (character == '\n') ||
                    (character == '\r') ||
                    (character >= '\u0020' && character <= '\uD7FF') ||
                    (character >= '\uE000' && character <= '\uFFFD')) {
                filtered[index++] = character;
            }
        }
        return new String(filtered, 0, index);
    }
}

Generated by  Doxygen 1.6.0   Back to index