Java tutorial
/* * Zettelkasten - nach Luhmann ** Copyright (C) 2001-2014 by Daniel Ldecke (http://www.danielluedecke.de) * * Homepage: http://zettelkasten.danielluedecke.de * * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, see <http://www.gnu.org/licenses/>. * * * Dieses Programm ist freie Software. Sie knnen es unter den Bedingungen der GNU * General Public License, wie von der Free Software Foundation verffentlicht, weitergeben * und/oder modifizieren, entweder gem Version 3 der Lizenz oder (wenn Sie mchten) * jeder spteren Version. * * Die Verffentlichung dieses Programms erfolgt in der Hoffnung, da es Ihnen von Nutzen sein * wird, aber OHNE IRGENDEINE GARANTIE, sogar ohne die implizite Garantie der MARKTREIFE oder * der VERWENDBARKEIT FR EINEN BESTIMMTEN ZWECK. Details finden Sie in der * GNU General Public License. * * Sie sollten ein Exemplar der GNU General Public License zusammen mit diesem Programm * erhalten haben. Falls nicht, siehe <http://www.gnu.org/licenses/>. */ package de.danielluedecke.zettelkasten.database; import de.danielluedecke.zettelkasten.util.Constants; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.IllegalAddException; import org.jdom2.IllegalDataException; import org.jdom2.IllegalNameException; /** * * @author danielludecke */ public class Synonyms { /** * XML Document that Stores the main data */ private Document synonymsFile; private boolean modified; /** * XML document that is used as backup */ private Document backupdoc = null; public Synonyms() { clear(); } /** * Clears the XML document and creates a dummy-backup of the document, in case the original * XML-document contains data. */ public final void clear() { // check whether backup document exists, whether autokorrektur-document exists and whether // the autokorrektur-document has any data. only in this case we create a backup if (synonymsFile != null && synonymsFile.getRootElement().getContentSize() > 1) { // create new backup doc backupdoc = new Document(new Element("backup_synonyms")); // copy content backupdoc.getRootElement().addContent(synonymsFile.getRootElement().cloneContent()); } synonymsFile = new Document(new Element("synonyms")); // reset modified state modified = false; } /** * This method checks whether the XML document is ok or corrupted. in case there have been * jdom-errors when adding new elements, the XML document {@link #synonymsFile} might * be empty, while the backup-document {@link #backupdoc} has data. In this case, * {@code false} is returned. If the XML-document is ok, {@code true} is returned. * @return {@code true} if the main XML-document is ok, {@code false}if it might be corrupted. * <br><br> * You can use {@link #restoreDocument() restoreDocument()} to restore a corrupted document. */ public boolean isDocumentOK() { // check whether we have any XML-document at all. proceed only, if we have no document // of if the XML-document does not contain data if ((null == synonymsFile) || (synonymsFile.getRootElement().getContentSize() < 1)) { // now check whether we have a backup of the XML document, which has content if ((backupdoc != null) && (backupdoc.getRootElement().getContentSize() > 0)) { // if so, the backup contains data that the main document does not has // so, we assume the document is *not* ok return false; } } // else everything is fine return true; } /** * In case we have a corrupted XML document with a backup document that has data * (see {@link #isDocumentOK() isDocumentOK()}), we can restore the backupped data * with this method.<br><br> * So, this method copies back the content of the {@link #backupdoc} to the * original XML document {@link #synonymsFile}. */ public void restoreDocument() { // check whether we have a backup document that also contains data if ((backupdoc != null) && (backupdoc.getRootElement().getContentSize() > 1)) { // if we have it, create new main XML document synonymsFile = new Document(new Element("synonyms")); // and copy the content of the backup document to it synonymsFile.getRootElement().addContent(backupdoc.getRootElement().cloneContent()); } } /** * Sets the document, e.g. after loading the settings * @param d the document with the synonyms-data */ public void setDocument(Document d) { synonymsFile = d; // check whether backup document exists, whether autokorrektur-document exists and whether // the autokorrektur-document has any data. only in this case we create a backup if (synonymsFile != null && synonymsFile.getRootElement().getContentSize() > 1) { // create new backup doc backupdoc = new Document(new Element("backup_synonyms")); // copy content backupdoc.getRootElement().addContent(synonymsFile.getRootElement().cloneContent()); } } /** * Gets the xml-document that contains the synonyms-data * @return the xml-document with the synonyms-data */ public Document getDocument() { return synonymsFile; } /** * This method returns the size of the xml data files * @return the size of the data file */ public int getCount() { return synonymsFile.getRootElement().getContentSize(); } /** * Retrieves all synonyms - both index words and related synonyms - that * currently are in the synonyms data base. * * @return all current synonyms as string array. */ public String[] getAllSynonyms() { LinkedList<String> synlist = new LinkedList<String>(); // iterate all synonyms elements for (int i = 0; i < getCount(); i++) { // retrieve synonyms String[] syns = getSynonymLine(i, true); // add to list for (String sy : syns) { // check for doubles if (!synlist.contains(sy)) synlist.add(sy); } } return synlist.toArray(new String[synlist.size()]); } public void appendSynonyms(Document syndoc) { int count = syndoc.getRootElement().getContentSize(); for (int i = 0; i < count; i++) { String[] synline = getSynonymLine(syndoc, i, false); addSynonym(synline); } } public void addSynonym(String[] synline) { // we need at least two elements in the array: the original word and at least one synonym if (null == synline || synline.length < 2) { return; } // if the synonyms-index-word already exists, don't add it... if (getSynonymPosition(synline[0]) != -1) { return; } // create new synonyms element Element synonym = new Element(Daten.ELEMENT_ENTRY); try { // trim spaces synline[0] = synline[0].trim(); // set the original word as value-attribute to the "entry"-element synonym.setAttribute("indexword", synline[0]); // now go through the rest of the string-array for (int cnt = 1; cnt < synline.length; cnt++) { // create a sub-child "syn" for each further synonym Element syn = new Element("syn"); // set text from string array syn.setText(synline[cnt].trim()); // add child to synonym-element synonym.addContent(syn); } // finally, add new element to the document synonymsFile.getRootElement().addContent(synonym); setModified(true); } catch (IllegalNameException ex) { Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage()); } catch (IllegalDataException ex) { Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage()); } catch (IllegalAddException ex) { Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage()); } } public String[] getSynonymLine(Document doc, int nr, boolean matchcase) { // get element Element syn = retrieveElement(doc, nr); // init return value String[] retval = null; // if we have a valid element, go on if (syn != null) { // get list of child-element with synonyms List l = syn.getChildren(); // create array retval = new String[l.size() + 1]; // first element of the array is the index word if (matchcase) { // retrieve indexword-attribute String attr = syn.getAttributeValue("indexword"); // check whether value ok if (attr != null) { // then use it for array retval[0] = attr; } else { // else log error message, telling the number of the corrupted synonym-data Constants.zknlogger.log(Level.WARNING, "No index word for synonym {0} found.", String.valueOf(nr)); // and return null return null; } } else { // retrieve indexword-attribute String attr = syn.getAttributeValue("indexword"); // check whether value ok if (attr != null) { // then use it for array retval[0] = attr.toLowerCase(); } else { // else log error message, telling the number of the corrupted synonym-data Constants.zknlogger.log(Level.WARNING, "No index word for synonym {0} found.", String.valueOf(nr)); // and return null return null; } } // following elements are the synonyms. therefore, copy the children's text to the array for (int cnt = 0; cnt < l.size(); cnt++) { // get the element Element e = (Element) l.get(cnt); // get the element's text if (matchcase) { retval[cnt + 1] = e.getText(); } else { retval[cnt + 1] = e.getText().toLowerCase(); } } } return retval; } /** * This method returns a synonym (as index-word) with its related synonyms. The return-value * is a string-array with the first element being the index-word, and the following elements * being the related synonyms. * * @param nr the number of the requested synonym, with a range from 0 to {@link #getCount()}-1 * @param matchcase use {@code true} if the synonyms (strings in the return array) * should be returned in original case-letters. * use {@code false} if they should be returned in lower-case-letters * @return a string-array with the first element being the index-word, and the following elements * being the related synonyms; or {@code null}, if no synonym was found */ public String[] getSynonymLine(int nr, boolean matchcase) { return getSynonymLine(synonymsFile, nr, matchcase); } /** * This method sets a new synonm-line, i.e. a synonym (as index-word) with its related synonyms. * The new synonyms have to passed as string-parameter {@code synline}. * * @param nr the number of the requested synonym, with a range from 0 to (getCount()-1) * @param synline a string-array with the first element being the index-word, and the following elements * being the related synonyms */ public void setSynonymLine(int nr, String[] synline) { // get element Element synonym = retrieveElement(nr); // remove all child-content (i.e. all synonyms) synonym.removeContent(); try { // set the original word as value-attribute to the "entry"-element synonym.setAttribute("indexword", synline[0]); // now go through the rest of the string-array for (int cnt = 1; cnt < synline.length; cnt++) { // create a sub-child "syn" for each further synonym Element syn = new Element("syn"); // set text from string array syn.setText(synline[cnt]); // add child to synonym-element synonym.addContent(syn); setModified(true); } } catch (IllegalDataException ex) { Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage()); } catch (IllegalNameException ex) { Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage()); } } public boolean mergeSynonymLines(String originalLineIndexWord, String mergedLineIndexWord) { return mergeSynonymLines(getSynonymPosition(originalLineIndexWord), getSynonymPosition(mergedLineIndexWord)); } /** * This method merges two synonym-lines, where the line given by the index-number {@code originalLine} * remains, while the line with the index-number {@code mergedLine} will be appended to the synonyms * of the {@code originalLine}. * * @param originalLine the index-number of the original line that should remain in the database * @param mergedLine the index-number of the synonym-line that should be appended to the original line. * @return */ public boolean mergeSynonymLines(int originalLine, int mergedLine) { // check for valid parameters if (originalLine < 0 || originalLine >= getCount() || mergedLine < 0 || mergedLine >= getCount()) { return false; } // retrieve original synonyms line String[] oriline = getSynonymLine(originalLine, true); // retrieve apped-synonyms line String[] appendline = getSynonymLine(mergedLine, true); // check for valid values if (null == oriline || null == appendline) { return false; } // create array-list that will contain new synonym line List<String> newline = new ArrayList<String>(); // add all "old", original synonyms newline.addAll(Arrays.asList(oriline)); // now append all "merged" synonyms newline.addAll(Arrays.asList(appendline)); // set new line setSynonymLine(originalLine, newline.toArray(new String[newline.size()])); // remove old synonym-line synonymsFile.getRootElement().removeContent(mergedLine); setModified(true); return true; } /** * This method sets a new index-word to the synonyms-line with the number {@code nr}. * * @param nr the number of the synonyms-line, where the index-word should be changed * @param indexword the new index-word, as string */ public void setIndexWord(int nr, String indexword) { // get element Element synonym = retrieveElement(nr); // set the original word as value-attribute to the "entry"-element synonym.setAttribute("indexword", indexword); setModified(true); } /** * This method adds a new synonym {@code appendsyn} to an existing synonym-line {@code nr}. * @param nr the number of the existing synonym-line * @param appendsyn the new synonym that should be appended to that line. */ public void appendSingleSynonym(int nr, String appendsyn) { // get element Element synonym = retrieveElement(nr); // chekc for valid value if (synonym != null) { try { // create a sub-child "syn" for each further synonym Element syn = new Element("syn"); // set text from string array syn.setText(appendsyn); // add child to synonym-element synonym.addContent(syn); setModified(true); } catch (IllegalAddException ex) { Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage()); } catch (IllegalDataException ex) { Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage()); } } } /** * This method returns a synonym (as index-word) with its related synonyms. The return-value * is a string-array with the first element being the index-word, and the following elements * being the related synonyms. * <br><br> * If a complete synonyme-line from <b>any word</b> is requested, * use {@link #getSynonymLineFromAny(java.lang.String) #getSynonymLineFromAny()} * * @param indexword string-value of the synonym (original- or index-word) which is searched for in the list * @param matchcase * @return a string-array with the first element being the index-word, and the following elements * being the related synonyms; or {@code null}, if no synonym was found */ public String[] getSynonymLine(String indexword, boolean matchcase) { // retrieve position of the index-word int pos = getSynonymPosition(indexword); // if we found an index-word, return the synonyms, else return null if (pos != -1) { return getSynonymLine(pos, matchcase); } else { return null; } } private Element retrieveElement(Document doc, int pos) { // create a list of all elements from the given xml file try { List<?> elementList = doc.getRootElement().getContent(); // and return the requestet Element try { return (Element) elementList.get(pos); } catch (IndexOutOfBoundsException e) { return null; } } catch (IllegalStateException e) { Constants.zknlogger.log(Level.WARNING, e.getLocalizedMessage()); return null; } } /** * This function retrieves an element of a xml document at a given * position. used for other methods like getAuthor or * getKeyword. The position is a value from 0 to (size-1). * * @param pos the position of the element, ranged from 0 to (document-size - 1) * @return the element if a match was found, otherwise null */ private Element retrieveElement(int pos) { return retrieveElement(synonymsFile, pos); } /** * This method returns the position of a synonym (original or index word) in the XML file * if the synonym doesn't exist, the return value is -1. * <br><br> * * @param indexword string-value of the synonym (original- or index-word) which is searched for in the list * @return the position of the synonym or -1 if no match was found */ public int getSynonymPosition(String indexword) { // trim spaces indexword = indexword.trim(); // create a list of all author elements from the author xml file try { List<?> synList = synonymsFile.getRootElement().getContent(); // and an iterator for the loop below Iterator<?> iterator = synList.iterator(); // counter for the return value if a found synonym matches the parameter int cnt = 0; while (iterator.hasNext()) { Element syn = (Element) iterator.next(); // if synonym-index-word matches the parameter string, return the position if (indexword.equalsIgnoreCase(syn.getAttributeValue("indexword"))) { return cnt; } // else increase counter cnt++; } // if no author was found, return -1 return -1; } catch (IllegalStateException e) { Constants.zknlogger.log(Level.WARNING, e.getLocalizedMessage()); return -1; } } /** * This method returns the position of a synonym in the XML file, independent from wether the requested * word is an index-word or a related synonym. if the synonym doesn't exist, the return value is -1. * <br><br> * If the position of a certain index-word only is requestes, use {@link #getSynonymPosition(java.lang.String) getSynonymPosition(java.lang.String)} * instead. * <br><br> * If a complete synonyme-line is requested, use {@link #getSynonymLine(java.lang.String) #getSynonymLine(java.lang.String)} * or {@link #getSynonymLineFromAny(java.lang.String) getSynonymLineFromAny(java.lang.String)}. * * @param synonym string-value of the synonym (wither original- or index-word, or any related synonym) * which is searched for in the list. * @param matchcase * @return the position of the synonym or -1 if no match was found */ public int findSynonym(String synonym, boolean matchcase) { // go through all entries for (int cnt = 0; cnt < getCount(); cnt++) { // get each synonym-line String[] synline = getSynonymLine(cnt, matchcase); // check for valid value if (synline != null) { // if the array contains the requested synonym, return line-position for (String s : synline) { if (s.equalsIgnoreCase(synonym)) { return cnt; } } } } return -1; } /** * This method searches all index-words of the synonyms-file and compares them to the * parameter {@code synonym}. If a match was found, i.e. if the parameter {@code synonym} is * an index-word, {@code true} is returned, {@code false} otherwise. * * @param synonym string-value which should be checked whether it is an index-word or not * @param matchcase * @return {@code true} if the parameter {@code synonym} is an index-word, {@code false} otherwise */ public boolean isIndexWord(String synonym, boolean matchcase) { // go through all entries for (int cnt = 0; cnt < getCount(); cnt++) { // get each synonym-line String[] synline = getSynonymLine(cnt, matchcase); // if the index-word of the array contains the requested synonym, return true if (synline != null && synline[0].equalsIgnoreCase(synonym)) { return true; } } return false; } /** * This method searches through all synonyms in the data-file and returns the first index-word * when a related synonym of that index-word matches {@code synvalue}.<br><br><b>Note:</b> This method * excludes index-words from the search! Only non-index-words of each synonym-line are checked for * matching the parameter {@code synvalue}. * * @param synvalue the synonym where we want to retrieve the related index-word * @param matchcase whether the check for the synonyms should be case sensitive {@code true} or not. * @return the index-word of the related synonym {@code synvalue}, or {@code null} if no index-word * was found. */ public String getIndexWord(String synvalue, boolean matchcase) { // go through all entries for (int cnt = 0; cnt < getCount(); cnt++) { // get each synonym-line String[] synline = getSynonymLine(cnt, matchcase); // go through array to find the value "synvalue"... if (synline != null && synline.length > 1) { for (int c = 1; c < synline.length; c++) { // if we found the synvalue, return the related indexword if (synline[c].equalsIgnoreCase(synvalue)) { return synline[0]; } } } } // else return null... return null; } /** * This method returns the related synonyms of a given synonym in the XML file, independent from wether the requested * word is an index-word or a related synonym. if the synonym doesn't exist, the return value is null. * <br><br> * If a complete synonyme-line from the <b>index-word</b> only is requested, * use {@link #getSynonymLine(java.lang.String) #getSynonymLine(java.lang.String)} * * @param synonym string-value of the synonym (wither original- or index-word, or any related synonym) * which is searched for in the list. * @param matchcase * @return the other related synonyms as string array, with the parameter {@code synonym} <b>included</b>, * or null if {@code synonym} wasn't found. */ public String[] getSynonymLineFromAny(String synonym, boolean matchcase) { // go through all entries for (int cnt = 0; cnt < getCount(); cnt++) { // get each synonym-line String[] synline = getSynonymLine(cnt, matchcase); // check for valid value if (synline != null) { // if the array contains the requested synonym, return the synonym-line (string-array) for (String s : synline) { if (s.equalsIgnoreCase(synonym)) { return synline; } } } } return null; } /** * sets the modified state of the bookmark-data * @param m true when the bookmark-data was modified, or false if modifications were saved. */ public void setModified(boolean m) { modified = m; } /** * returns the modified state of the bookmark-data * @return {@code true} if the bookmark-data was modified, false if it's unchanged */ public boolean isModified() { return modified; } }