package com.applang.components;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import javax.swing.Box;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.gjt.sp.jedit.View;
import android.content.ContentUris;
import android.content.ContentValues;
import android.net.Uri;
import com.applang.Util.Job;
import com.applang.berichtsheft.BerichtsheftApp;
import com.applang.berichtsheft.plugin.BerichtsheftPlugin;
import com.applang.components.DataView.DataModel;
import com.applang.provider.NotePad;
import com.applang.provider.NotePadProvider;
import com.applang.provider.NotePad.NoteColumns;
import static com.applang.SwingUtil.*;
import static com.applang.Util.*;
import static com.applang.Util1.*;
import static com.applang.Util2.*;
import static com.applang.PluginUtils.*;
public class NotePicker extends ActionPanel
{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
BerichtsheftApp.loadSettings();
setupSpellChecker(BerichtsheftApp.applicationDataPath());
final DataView dataView = new DataView();
final TextToggle textToggle = new TextToggle()
.createBufferedTextArea("velocity", "/modes/velocity_pure.xml");
textToggle.getTextEdit().installSpellChecker();
String title = "Berichtsheft database";
NotePicker notePicker = new NotePicker(dataView, textToggle,
null,
title, 1);
createAndShowGUI(title,
new Dimension(800, 400),
Behavior.EXIT_ON_CLOSE,
notePicker,
new Function<Component>() {
public Component apply(Object...params) {
JSplitPane splitPane = splitPane(JSplitPane.VERTICAL_SPLIT,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
}
});
splitPane.setResizeWeight(0.5);
splitPane.setOneTouchExpandable(true);
Component target = textToggle.getUIComponent();
splitPane.setTopComponent(target);
Component c = findFirstComponent(dataView, "south");
if (c != null)
dataView.remove(c);
splitPane.setBottomComponent(dataView);
return splitPane;
}
});
}
});
}
@Override
public void start(Object... params) {
if (!usingJdbc())
initialize(dataView.getUriString());
else {
dbFilePath = getSetting("database", "");
if (fileExists(new File(dbFilePath)))
initialize(dbFilePath);
}
}
@Override
public void finish(Object... params) {
if (!usingJdbc())
dataView.nosync();
getTextToggle().getTextEdit().uninstallSpellChecker();
try {
if (getCon() != null)
getCon().close();
} catch (SQLException e) {
handleException(e);
}
if (usingJdbc())
putSetting("database", dbFilePath);
super.finish(params);
}
private DataView dataView;
public void setDataView(DataView dataView) {
this.dataView = dataView;
initialize(dataView.getUriString());
}
public boolean usingJdbc() {
return dataView == null;
}
private JTextField date = null;
public NotePicker(DataView dataView, TextToggle textArea, Object... params) {
super(textArea, params);
textArea.setOnTextChanged(new Job<JComponent>() {
public void perform(JComponent t, Object[] params) throws Exception {
setDirty(true);
}
});
if (!usingJdbc() && dataView.getUri() == null)
dataView.resetDataConfiguration();
this.dataView = dataView;
}
public void installNotePicking(Container container) {
container.removeAll();
addButton(container, ActionType.DATABASE.index(), new NoteAction(ActionType.DATABASE));
date = new JTextField(20);
date.setHorizontalAlignment(JTextField.CENTER);
addSelectAllObserver(date);
container.add(date);
date.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
super.keyTyped(e);
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER)
clickAction(8);
else
super.keyPressed(e);
}
});
addButton(container, ActionType.CALENDAR.index(), new NoteAction(ActionType.CALENDAR));
installBrowsing(container);
installAddRemove(container, "note");
installUpdate(container);
clear();
}
@SuppressWarnings("rawtypes")
public void installBrowsing(Container container) {
comboBoxes = new JComboBox[] {new JComboBox()};
comboBoxes[0].setEditable(true);
container.add(comboBoxes[0]);
JTextField textField = comboEdit(0);
addSelectAllObserver(textField);
textField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER)
clickAction(ActionType.PICK.index());
}
});
addButton(container, ActionType.PICK.index(), new NoteAction(ActionType.PICK));
addButton(container, ActionType.FIRST.index(), new NoteAction(ActionType.FIRST));
addButton(container, ActionType.PREVIOUS.index(), new NoteAction(ActionType.PREVIOUS));
addButton(container, ActionType.NEXT.index(), new NoteAction(ActionType.NEXT));
addButton(container, ActionType.LAST.index(), new NoteAction(ActionType.LAST));
}
public void installBausteinEditing(Container container) {
container.removeAll();
date = null;
addButton(container, ActionType.DATABASE.index(), new NoteAction(ActionType.DATABASE));
addButton(this, ActionType.STRUCT.index(), new NoteAction(ActionType.STRUCT));
container.add( Box.createHorizontalStrut(3) );
addToggle(this, ActionType.TOGGLE2.index(), new NoteAction(ActionType.TOGGLE2));
container.add( Box.createHorizontalStrut(3) );
installBrowsing(container);
installAddRemove(container, "baustein");
installUpdate(container);
clear();
}
class NoteAction extends CustomAction
{
public NoteAction(ActionType type) {
super(type);
if (type.equals(ActionType.TOGGLE2)) {
putValue(NAME, type.name(1));
}
}
public NoteAction(String text) {
super(text);
}
@Override
protected void action_Performed(ActionEvent ae) {
String dateString = getDate();
ActionType type = (ActionType)getType();
switch (type) {
case DATABASE:
updateOnRequest();
dbFilePath = chooseDatabase(dbFilePath);
break;
case CALENDAR:
dateString = pickDate(dateString);
date.requestFocusInWindow();
break;
case FIRST:
case PREVIOUS:
case NEXT:
case LAST:
browse(type);
break;
case PICK:
if (usingJdbc()) {
searchPattern = getPattern();
finder.keyLine(searchPattern);
pickNote(dateString, searchPattern);
}
else if (pkColumn > -1) {
finder.keyLine();
Long time = toTime(dateString, DatePicker.calendarFormat);
if (time != null) {
pkRow = finder.pointer(time, getTitle());
browse(type);
}
}
break;
case DATE:
date.requestFocusInWindow();
break;
case TITLE:
comboBoxes[0].requestFocusInWindow();
break;
case INSERT:
break;
case DELETE:
deleteSelection();
break;
case TOGGLE2:
toggle(this, getTextToggle().getTextEdit().toggler);
break;
case STRUCT:
break;
default:
return;
}
}
}
private void clear() {
if (usingJdbc())
refreshWith(null);
else {
setDate("");
setTitle("");
setText("");
}
enableBrowseActions();
}
public void enableBrowseActions() {
enableAction(ActionType.FIRST.index(), pkRow > 0);
enableAction(ActionType.PREVIOUS.index(), pkRow > 0);
enableAction(ActionType.NEXT.index(), pkRow < lastRow() && pkRow > -1);
enableAction(ActionType.LAST.index(), pkRow < lastRow() && pkRow > -1);
}
private DataModel dataModel() {
if (dataView == null)
return null;
return (DataModel) dataView.getTable().getModel();
}
private int convertRowIndexToModel(int rowIndex) {
return dataView.getTable().convertRowIndexToModel(rowIndex);
}
private DataAdapter dataAdapter = null;
private String uriString = NoteColumns.CONTENT_URI.toString();
private Object pk = null;
public int pkColumn = -1, pkRow = -1;
private boolean createProvider() {
String dbPath = dataView.getDataConfiguration().getPath();
dataAdapter = new DataAdapter(NotePad.AUTHORITY, new File(dbPath), uriString);
ValMap info = dataAdapter.info;
if (info.size() > 0) {
pk = info.get("PRIMARY_KEY");
pkColumn = info.getList("name").indexOf(pk);
if (pkColumn < 0) {
BerichtsheftPlugin.consoleMessage("dataview.no-primary-key.message");
}
}
else {
BerichtsheftPlugin.consoleMessage("dataview.no-info-for-table.message", dataAdapter.getTableName());
pkColumn = -1;
}
return pkColumn > -1;
}
public Object pkValue() {
if (pkRow < 0)
return null;
int rowIndex = convertRowIndexToModel(pkRow);
Object[] row = dataModel().getValues(false, rowIndex);
return row[pkColumn];
}
public int lastRow() {
DataModel model = dataModel();
if (model == null)
return -1;
else
return model.getRowCount() - 1;
}
public void setRow(Object pkValue) {
DataModel model = dataModel();
for (int i = 0; i < model.getRowCount(); i++) {
Object[] row = dataModel().getValues(false, i);
if (row[pkColumn].equals(pkValue)) {
pkRow = i;
break;
}
}
}
private void initialize(String dbString) {
if (usingJdbc()) {
if (openConnection(dbString)) {
retrieveTitles();
if (finder != null) {
searchPattern = allTitles;
finder.keyLine(searchPattern);
setDate(formatDate(
0,
finder.keys.length > 0 ? finder
.epochFromKey(finder.keys[0]) : now()));
pickNote(getDate(), searchPattern);
}
}
}
else {
String tableName = dbTableName(dbString);
switch (NotePadProvider.tableIndex(tableName)) {
case 1:
installBausteinEditing(this);
getTextToggle().toggle(false, null);
break;
default:
installNotePicking(this);
getTextToggle().toggle(true, null);
break;
}
uriString = NotePadProvider.contentUri(tableName).toString();
refresh();
}
JComponent jc = (JComponent) getParent();
if (jc != null)
setWindowTitle(this, String.format(
"Berichtsheft database : %s",
trimPath(dbString, jc.getWidth() / 2, jc.getFont(), jc)));
}
public void refresh() {
if (usingJdbc()) {
finder.keyLine(searchPattern);
pickNote(getDate(), finder.pattern);
retrieveTitles();
}
else if (!noRefresh) {
clear();
if (createProvider()) {
dataView.populate(dataAdapter, null, null, DEFAULT_SORT_ORDER);
retrieveTitles();
browse(ActionType.PICK);
}
}
}
private static final String DEFAULT_SORT_ORDER = "created,title";
@Override
protected String chooseDatabase(String dbName) {
if (usingJdbc()) {
File dbFile = DataView.chooseDb(null, false, dbName, true);
if (dbFile != null) {
dbName = dbFile.getPath();
initialize(dbName);
}
}
else {
View view = getView() instanceof View ? (View) getView() : null;
if (dataView.configureData(view, false)) {
dbName = dataView.getUriString();
initialize(dbName);
}
}
return dbName;
}
private String pickDate(String dateString) {
Long[] times = timeLine();
if (times.length > 0)
times = arrayextend(times, true, times[0] - getMillis(1) + 1);
dateString = new DatePicker(NotePicker.this, dateString, times).getDateString();
NotePicker.this.requestFocus();
setDate(dateString);
return dateString;
}
@Override
protected void updateOnRequest() {
Object item = getItem();
if (isItemValid(item)) {
save(item, false);
}
}
private long[] getTime(String dateString) {
int kind = DatePicker.kindOfDate(dateString);
switch (kind) {
case 3:
return DatePicker.monthInterval(dateString, 1);
case 2:
return DatePicker.weekInterval(dateString, 1);
default:
Date date = parseDate(dateString);
return new long[] {date == null ? now() : date.getTime()};
}
}
private boolean bausteinEditing() {
return date == null;
}
public String getDate() {
if (bausteinEditing())
return "";
String text = date.getText();
return text;
}
public void setDate(final String text) {
try {
CustomAction.blocked(new Job<Void>() {
public void perform(Void v, Object[] params) throws Exception {
if (!bausteinEditing())
date.setText(text);
}
}, null);
} catch (Exception e) {}
}
@SuppressWarnings("unchecked")
private void retrieveTitles() {
try {
String title = getTitle();
comboBoxes[0].removeAllItems();
if (usingJdbc()) {
PreparedStatement ps = getCon().prepareStatement(
"select distinct title from notes order by title");
ResultSet rs = ps.executeQuery();
while (rs.next())
comboBoxes[0].addItem(rs.getString(1));
comboBoxes[0].addItem(itemAll);
comboBoxes[0].addItem(itemBAndB);
}
else {
Object[][] res = dataAdapter.query(uriString,
(String[])null,
String.format("select distinct title from %s order by title", dataAdapter.getTableName()));
for (Object[] row : res) {
comboBoxes[0].addItem(row[0]);
}
}
setTitle(title);
} catch (Exception e) {}
}
public void setTitle(final String t) {
try {
CustomAction.blocked(new Job<Void>() {
public void perform(Void v, Object[] params) throws Exception {
comboEdit(0).setText(t);
}
}, null);
} catch (Exception e) {}
}
public String getTitle() {
return comboEdit(0).getText();
}
public static final String allDates = "";
public static final String allTitles = SOMETHING_OR_NOTHING_REGEX;
public static final String itemAll = "-- all --";
public static final String bAndB = "(?i)(bemerk\\w*|bericht\\w*)";
public static final String itemBAndB = "Berichte & Bemerkungen";
public void setPattern(String p) {
finder.pattern = p;
for (Object value : finder.specialPatterns.getValues())
if (p.equals(value))
p = stringValueOf(finder.specialPatterns.getKey(value));
setTitle(p);
}
public String getPattern() {
String c = getTitle();
for (Object key : finder.specialPatterns.getKeys())
if (c.equals(key))
c = stringValueOf(finder.specialPatterns.getValue(key));
return c;
}
public Long[] timeLine(Object... params) {
ArrayList<Long> timelist = alist();
try {
if (usingJdbc()) {
String pattern = param(allTitles, 0, params);
PreparedStatement ps = getCon()
.prepareStatement(
"select distinct created FROM notes where title regexp ? order by created");
ps.setString(1, pattern);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
timelist.add(rs.getLong(1));
}
rs.close();
}
else {
Object[][] res = dataAdapter.query(uriString,
(String[])null,
String.format("select distinct created FROM %s order by created", dataAdapter.getTableName()));
for (Object[] row : res) {
timelist.add((Long) row[0]);
}
}
} catch (Exception e) {}
return timelist.toArray(new Long[0]);
}
String searchPattern = allTitles;
public class NoteFinder
{
public BidiMultiMap specialPatterns = new BidiMultiMap();
public NoteFinder() {
specialPatterns.add(itemAll, allTitles);
specialPatterns.add(itemBAndB, bAndB);
}
String pattern = "";
long[] epoch = new long[0];
String[] keys = null;
public String[] keyLine(String...pattern) {
ValList keylist = vlist();
try {
if (usingJdbc()) {
PreparedStatement ps = getCon()
.prepareStatement(
"select created,title FROM notes where title regexp ? order by " + DEFAULT_SORT_ORDER);
ps.setString(1, pattern[0]);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
keylist.add(keyValue(rs.getLong(1), rs.getString(2)));
}
rs.close();
}
else if (isAvailable(0, pattern)){
Object[][] result = dataAdapter.query(uriString,
strings("created", "title"),
"title like ?",
strings(pattern[0]),
DEFAULT_SORT_ORDER);
for (Object[] res : result) {
keylist.add(keyValue((Long)res[0], (String)res[1]));
}
}
else {
DataModel model = dataModel();
int[] index = {
model.columns.indexOf("created"),
model.columns.indexOf("title")
};
for (int i = 0; i < model.getRowCount(); i++) {
Object[] values = model.getValues(false, i);
keylist.add(keyValue((Long)values[index[0]], (String)values[index[1]]));
}
}
} catch (Exception e) {}
return this.keys = toStrings(keylist);
}
public String keyValue(long time, String title) {
return time + "_" + title;
}
public long epochFromKey(String key) {
return Long.parseLong(key.substring(0, key.indexOf('_')));
}
public String titleFromKey(String key) {
return key.substring(key.indexOf('_') + 1);
}
private class KeyComparator implements Comparator<String>
{
public KeyComparator(boolean partial) {
this.partial = partial;
}
private boolean partial;
@Override
public int compare(String o1, String o2) {
long t1 = epochFromKey(o1);
long t2 = epochFromKey(o2);
if (t1 < t2)
return -1;
else if (t1 > t2)
return 1;
else if (partial)
return 0;
String c1 = titleFromKey(o1);
String c2 = titleFromKey(o2);
return c1.compareTo(c2);
}
};
private KeyComparator comparator = new KeyComparator(false);
private KeyComparator partialComparator = new KeyComparator(true);
private int criterion(long epoch, String pattern) {
String value = keyValue(epoch, pattern);
if (specialPatterns.getValues().contains(pattern)) {
ArrayList<Integer> a = alist();
ArrayList<Integer> b = alist();
ArrayList<Integer> c = alist();
int comp = 0;
for (int i = 0; i < this.keys.length; i++) {
String key = this.keys[i];
if ((comp = partialComparator.compare(key, value)) == 0 && titleFromKey(key).matches(pattern))
a.add(i);
else if (comp < 0)
b.add(i);
else
c.add(i);
}
if (a.size() > 0)
return Collections.min(a);
else if (c.size() > 0)
return -Collections.min(c) - 1;
else if (b.size() > 0)
return -Collections.max(b) - 2;
else
return -1;
}
else
return Arrays.binarySearch(this.keys, value, comparator);
}
private int pointer(int crit) {
if (crit < 0)
crit = -crit - 1;
return crit;
}
public int pointer(long time, String title) {
return pointer(criterion(time, title));
}
private int[] index = new int[2];
public boolean bunchAvailable(long... epoch) {
index[0] = criterion(epoch[0], pattern);
if (epoch.length > 1) {
index[1] = criterion(epoch[1] - 1, pattern);
return index[0] > -1 || index[0] != index[1];
}
else
return index[0] > -1;
}
public boolean nextBunchAvailable(long... epoch) {
if (epoch.length > 1) {
int pointer = pointer(epoch[1], pattern);
return pointer > 0 && pointer < keys.length;
} else
return pointer(epoch[0], pattern) < keys.length - 1;
}
public boolean previousBunchAvailable(long... epoch) {
int pointer = pointer(epoch[0], pattern);
return pointer > 0;
}
public String find(ActionType direct, long... epoch) {
boolean available = bunchAvailable(epoch);
int index = pointer(this.index[0]);
if (direct == ActionType.PICK)
if (!available) {
message(String.format("'%s' on %s not available", pattern, formatDate(epoch.length, epoch[0])));
if (index >= keys.length) {
direct = ActionType.PREVIOUS;
available = true;
}
else
direct = ActionType.NEXT;
}
boolean next = direct == ActionType.NEXT;
if (next && epoch.length > 1)
index = pointer(this.index[1]);
else {
if (available && direct != ActionType.PICK)
index += next ? 1 : -1;
}
if (index < 0 || index >= keys.length)
return null;
String key = keys[index];
long t = epochFromKey(key);
if (epoch.length > 1) {
if (epoch.length == 2)
this.epoch = DatePicker.weekInterval(formatDate(2, t), 1);
else
this.epoch = DatePicker.monthInterval(formatDate(3, t), 1);
}
else
this.epoch = new long[] {t};
this.pattern = titleFromKey(key);
return key;
}
}
public NoteFinder finder = new NoteFinder();
public void pickNote(String dateString, String pattern) {
clear();
setDate(dateString);
try {
finder.pattern = pattern;
finder.epoch = getTime(dateString);
if (nullOrEmpty(finder.epoch)) {
message(String.format("value for '%s' doesn't make sense", ActionType.DATE.description()));
date.requestFocus();
}
browse(ActionType.PICK);
} catch (Exception e) {
handleException(e);
setText("");
}
}
private boolean browseFree = true;
public void browse(ActionType direct) {
if (!browseFree)
return;
try {
browseFree = false;
updateOnRequest();
String text = "";
if (usingJdbc()) {
ResultSet rs = null;
if (finder.find(direct, finder.epoch) != null)
rs = query(finder.epoch);
enableAction(ActionType.PREVIOUS.index(),
finder.previousBunchAvailable(finder.epoch));
enableAction(ActionType.NEXT.index(),
finder.nextBunchAvailable(finder.epoch));
text = refreshWith(rs);
}
else if (pkColumn > -1) {
switch (direct) {
case FIRST:
pkRow = 0;
break;
case PREVIOUS:
pkRow--;
break;
case NEXT:
pkRow++;
break;
case LAST:
pkRow = lastRow();
break;
default:
pkRow = Math.min(Math.max(0, pkRow), lastRow());
break;
}
enableBrowseActions();
if (direct != ActionType.DELETE) {
int[] rows = dataView.getTable().getSelectedRows();
if (rows.length > 0 && direct == ActionType.PICK)
rows = ints(rows[0], rows[rows.length - 1]);
else
rows = ints(pkRow);
dataView.synchronizeSelection(rows, tablePopup, tableSelectionListener);
}
Object[] rec = (Object[]) select();
if (isAvailable(2, rec)) {
setDate(bausteinEditing() ?
null :
formatDate(1, (Long) rec[0]));
setTitle(stringValueOf(rec[1]));
text = stringValueOf(rec[2]);
}
else
clear();
}
setText(text);
}
finally {
browseFree = true;
}
}
private JPopupMenu tablePopup = newPopupMenu(
objects(ActionType.DELETE.description(), new NoteAction(ActionType.DELETE))
);
private ListSelectionListener tableSelectionListener = new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting())
return;
int sel = dataView.getTable().getSelectedRow();
if (sel > -1) {
pkRow = sel;
browse(ActionType.PICK);
}
}
};
private void deleteSelection() {
updateOnRequest();
int[] rows = dataView.getTable().getSelectedRows();
if (isAvailable(0, rows)) {
boolean refresh = false;
try {
begin_delete();
for (int i = rows.length - 1; i > -1; i--) {
pkRow = rows[i];
browse(ActionType.DELETE);
Boolean done = do_delete(getLongItem());
if (done != null)
refresh |= done;
}
}
catch (Throwable e) {}
finally {
end_delete();
}
if (refresh)
refresh();
}
}
@Override
public Object select(Object...args) {
args = reduceDepth(args);
boolean pkValue = notAvailable(0, args);
String dateString = param(null, 0, args);
String pattern = param(null, 1, args);
if (pkValue || notNullOrEmpty(dateString) || bausteinEditing())
try {
Long time = toTime(dateString, DatePicker.calendarFormat);
long[] interval = dayInterval(time, 1);
if (usingJdbc()) {
PreparedStatement ps = preparePicking(true, pattern, interval);
if (registerNotes(ps.executeQuery()) > 0) {
return records[0];
}
}
else {
Object[][] result = dataAdapter.query(uriString,
strings("created", "title", "note", "_id"),
whereClause(pkValue),
pkValue ?
strings("" + pkValue()) :
(bausteinEditing() ?
strings(pattern) :
strings("" + interval[0], "" + (interval[1] - 1), pattern)));
if (isAvailable(0, result)) {
return result[0];
}
}
} catch (Exception e) {
handleException(e);
}
return null;
}
private String whereClause(boolean pkValue) {
if (pkValue)
return pk + "=?";
else
return bausteinEditing() ?
"created is null and title = ?" :
"created between ? and ? and title like ?";
}
public static String shortFormat = "%s %s";
@Override
protected String toString(Object item) {
Object[] rec = (Object[]) item;
return String.format(shortFormat, rec[1], rec[0]);
}
@Override
protected String toLongString(Object item) {
Object[] rec = (Object[]) item;
return String.format(noteFormat2, rec[1], rec[0], rec[2]);
}
@Override
public boolean isItemValid(Object item) {
Object[] rec = (Object[]) item;
Date date = parseDate(stringValueOf(rec[0]));
return date != null && notNullOrEmpty(rec[1]);
}
@Override
protected Object getItem() {
return objects(getDate(), getTitle());
}
@Override
protected Object getLongItem() {
return objects(getDate(), getTitle(), getText());
}
@Override
protected void updateItem(boolean update, Object...args) {
Object[] rec = reduceDepth(args);
String text = getText();
if (isAvailable(0, rec))
updateOrAdd(false, text, rec);
else if (update)
updateOrAdd(false, text, (Object[]) getItem());
else {
rec = (Object[]) select(getItem());
if (isAvailable(2, rec))
setText(rec[2].toString());
}
}
private long updateOrAdd(boolean add, String text, Object...rec) {
if (usingJdbc())
return updateOrInsert(rec[0].toString(), rec[1].toString(), text, add);
else {
long time = toTime(rec[0].toString(), DatePicker.calendarFormat);
ValMap info = dataAdapter.info;
BidiMultiMap map = new BidiMultiMap();
map.putValue("note", text);
map.putValue("modified", now());
ContentValues values = contentValues(info, map.getKeys(), map.getValues().toArray());
if (add) {
values.putNull(pk.toString());
values.put("created", time);
values.put("title", rec[1].toString());
Uri uri = dataAdapter.insert(uriString, values);
return ContentUris.parseId(uri);
}
else {
rec = (Object[]) select(rec);
return dataAdapter.update(appendId(uriString, (Long) rec[3]), values);
}
}
}
@Override
protected boolean addItem(boolean refresh, Object item) {
Object[] rec = (Object[]) item;
String text = getText();
long id = updateOrAdd(true, text, rec);
if (refresh)
refresh();
return id > -1;
}
@Override
protected boolean removeItem(Object item) {
boolean done = false;
Object[] rec = (Object[]) select(item);
if (rec != null) {
if (usingJdbc())
done = remove(false, rec[1].toString(), rec[0].toString());
else
done = 0 < dataAdapter.delete(appendId(uriString, (Long) rec[3]));
}
if (done) {
clear();
refresh();
}
return done;
}
@Override
public void setText(String text) {
TextToggle textBums = getTextToggle();
updateText(textBums, text);
textBums.getTextEdit().undoManager.discardAllEdits();
}
public static String formatDate(int kind, long time) {
switch (kind) {
case 2:
return DatePicker.weekDate(weekInterval(new Date(time), 1));
case 3:
return DatePicker.monthDate(monthInterval(new Date(time), 1));
default:
return com.applang.Util.formatDate(time, DatePicker.calendarFormat);
}
}
public Date parseDate(String dateString) {
try {
return new Date(toTime(dateString, DatePicker.calendarFormat));
} catch (Exception e) {
return null;
}
}
@Override
public boolean openConnection(String dbPath, Object... params) {
try {
if (super.openConnection(dbPath, arrayextend(params, true, "notes")))
return true;
if ("sqlite".equals(getScheme()))
getStmt().execute("CREATE TABLE notes (" +
"_id INTEGER PRIMARY KEY," +
"title TEXT," +
"note TEXT," +
"created INTEGER," +
"modified INTEGER, " +
"UNIQUE (created, title))");
else if ("mysql".equals(getScheme()))
getStmt().execute("CREATE TABLE notes (" +
"_id BIGINT PRIMARY KEY," +
"title VARCHAR(40)," +
"note TEXT," +
"created BIGINT," +
"modified BIGINT, " +
"UNIQUE (created, title))");
return true;
} catch (Exception e) {
handleException(e);
return false;
}
}
private static final String orderClause = " order by created, title";
public PreparedStatement preparePicking(boolean record, String pattern, long... time) throws Exception {
String sql = "select " +
(record ?
"created,title,note,_id" :
"_id") +
" from notes";
PreparedStatement ps;
if (time.length < 2) {
ps = getCon().prepareStatement(sql + " where title regexp ? and created = ?");
ps.setString(1, pattern);
ps.setLong(2, time[0]);
}
else {
ps = getCon().prepareStatement(sql + " where created between ? and ? and title regexp ?" + orderClause);
ps.setLong(1, time[0]);
ps.setLong(2, time[1] - 1);
ps.setString(3, pattern);
}
return ps;
}
public int update(long id, String note) throws Exception {
PreparedStatement ps = getCon().prepareStatement("UPDATE notes SET note = ?, modified = ? where _id = ?");
ps.setString(1, note);
ps.setLong(2, now());
ps.setLong(3, id);
return ps.executeUpdate();
}
public int insert(long id, String note, String title, long time) throws Exception {
PreparedStatement ps = getCon().prepareStatement("INSERT INTO notes (_id,title,note,created,modified) VALUES (?,?,?,?,?)");
ps.setLong(1, id);
ps.setString(2, title);
ps.setString(3, note);
ps.setLong(4, time);
ps.setLong(5, now());
return ps.executeUpdate();
}
public int delete(String pattern, String dateString, boolean delete) throws Exception {
Connection con = getCon();
PreparedStatement ps;
Date date = parseDate(dateString);
if (date == null) {
if (delete)
ps = con.prepareStatement("delete from notes where title regexp ?");
else
ps = con.prepareStatement("select count(*) from notes where title regexp ?");
ps.setString(1, pattern);
}
else {
if (delete)
ps = con.prepareStatement("delete from notes where title regexp ? and created = ?");
else
ps = con.prepareStatement("select count(*) from notes where title regexp ? and created = ?");
ps.setString(1, pattern);
ps.setLong(2, date.getTime());
}
if (!delete) {
ResultSet rs = ps.executeQuery();
rs.next();
return rs.getInt(1);
}
else
return ps.executeUpdate();
}
public boolean remove(boolean ask, String pattern, String dateString) {
boolean retval = false;
try {
long[] interval = getTime(dateString);
if (interval.length == 1)
interval = dayInterval(interval[0], 1);
PreparedStatement ps = preparePicking(false, pattern, interval);
if (registerNotes(ps.executeQuery()) > 0) {
for (int i = 0; i < ids.length; i++)
if (!ask || confirmDelete(ids[i])) {
ps = getCon().prepareStatement("DELETE FROM notes where _id = ?");
ps.setLong(1, ids[i]);
ps.executeUpdate();
retval = true;
}
}
} catch (Exception e) {
handleException(e);
}
return retval;
}
private boolean confirmDelete(long id) throws SQLException {
PreparedStatement ps = getCon().prepareStatement("select title,created,note from notes where _id = ?");
ps.setLong(1, id);
ResultSet rs = ps.executeQuery();
boolean retval = false;
if (rs.next())
retval = JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
NotePicker.this, String.format("delete '%s' on '%s' : %s ?", rs.getString(1), rs.getLong(2), rs.getString(3)),
caption,
JOptionPane.YES_NO_OPTION);
rs.close();
return retval;
}
public long newId() throws Exception {
ResultSet rs = getStmt().executeQuery("SELECT max(_id) FROM notes");
long id = rs.next() ? rs.getLong(1) : -1;
rs.close();
return 1 + id;
}
public long updateOrInsert(String dateString, String pattern, String note, boolean insert) {
long id = -1;
try {
long time = toTime(dateString, DatePicker.calendarFormat);
PreparedStatement ps = preparePicking(false, pattern, dayInterval(time, 1));
ResultSet rs = ps.executeQuery();
if (rs.next()) {
id = rs.getLong(1);
update(id, note);
} else if (insert) {
id = newId();
insert(id, note, getTitle(), time);
}
rs.close();
} catch (Exception e) {
handleException(e);
return -1;
}
return id;
}
@SuppressWarnings("unused")
private void updateOrInsert(String pattern, String dateString) {
if (finder.epoch.length == 2) {
for (String[] record : listRecords(getText()))
updateOrInsert(record[0], record[1], record[2], true);
}
else
updateOrInsert(dateString, pattern, getText(), true);
}
private ResultSet query(long... time) {
ResultSet rs = null;
try {
String pattern = time.length > 1 ? searchPattern : finder.pattern;
PreparedStatement ps = preparePicking(true, pattern, time);
rs = ps.executeQuery();
} catch (Exception e) {
handleException(e);
}
return rs;
}
public Long[] ids = null;
public Object[][] records = null;
public int registerNotes(ResultSet rs) throws SQLException {
ArrayList<Long> idlist = alist();
ArrayList<Object[]> reclist = alist();
ResultSetMetaData rsmd = rs.getMetaData();
int cols = rsmd.getColumnCount();
while (rs.next()) {
if (cols > 1) {
ValList list = vlist();
for (int i = 1; i <= cols; i++)
list.add(rs.getObject(i));
reclist.add(list.toArray());
}
else
idlist.add(rs.getLong(1));
}
rs.close();
records = reclist.toArray(new Object[0][]);
ids = idlist.toArray(new Long[0]);
return cols > 1 ? records.length : ids.length;
}
private String refreshWith(ResultSet rs) {
String note = null;
try {
if (rs == null) {
setDate("");
return null;
}
int kind = finder.epoch.length;
if (kind > 1) {
if (registerNotes(rs) > 0) {
setDate(formatDate(kind, finder.epoch[0]));
setPattern(searchPattern);
note = all();
}
}
else {
if (rs.next()) {
setDate(formatDate(kind, rs.getLong(1)));
setTitle(rs.getString(2));
note = rs.getString(3);
}
rs.close();
}
return note;
} catch (Exception e) {
handleException(e);
return refreshWith(null);
}
finally {
setDirty(false);
// enableAction(ActionType.DELETE.index(), hasTextArea() && note != null);
}
}
public String all() throws Exception {
String all = String.format(wrapFormat, ":folding=explicit:");
for (int i = 0; i < records.length; i++) {
if (all.length() > 0)
all += "\n";
all += wrapNote(records[i]);
}
return all;
}
public String wrapNote(Object... params) throws Exception {
if (isAvailable(0, params) && params[0] instanceof Long) {
String dateString = formatDate(1, (Long)params[0]);
return String.format(noteFormat2, dateString,
param("", 1, params),
param("", 2, params));
}
else
return String.format(noteFormat1,
param("", 0, params),
param("", 1, params));
}
public static String wrapFormat = enclose(FOLD_MARKER[0], " %s ", FOLD_MARKER[1]);
public static Pattern wrapPattern =
Pattern.compile("(?s)" + enclose(FOLD_MARKER_REGEX[0], " (.*?) \\}\\}\\}", FOLD_MARKER_REGEX[1]));
public static String noteFormat1 = enclose(FOLD_MARKER[0], " %s\n%s\n", FOLD_MARKER[1]);
public static String noteFormat2 = enclose(FOLD_MARKER[0], " %s '%s'\n%s\n", FOLD_MARKER[1]);
public static Pattern notePattern1 =
Pattern.compile("(?s)" + enclose(FOLD_MARKER_REGEX[0], " ([^\\}]*?)\\n([^\\}]*?)\\n", FOLD_MARKER_REGEX[1]));
public static Pattern notePattern2 =
Pattern.compile("(?s)" + enclose(FOLD_MARKER_REGEX[0], " ([^\\}]*?) '([^\\}]*?)'\\n(.*?)\\n", FOLD_MARKER_REGEX[1]));
public boolean isWrapped(String text) {
return text.startsWith(FOLD_MARKER[0]) && text.endsWith(FOLD_MARKER[1]);
}
public String[][] listRecords(String text) {
ArrayList<String[]> list = alist();
for (MatchResult m : findAllIn(text, notePattern2))
list.add(strings(m.group(1), m.group(2), m.group(3)));
return list.toArray(new String[0][]);
}
}