/** * Copyright (C) 2010 EdgyTech LLC. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.edgytech.umongo; import com.edgytech.swingfast.*; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mongodb.BasicDBObject; import com.mongodb.CommandResult; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import java.awt.Component; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Iterator; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingUtilities; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; /** * * @author antoine */ public class DocView extends Zone implements EnumListener, TabInterface, Runnable { enum Item { docTree, tabDiv, tabTitle, tabClose, refresh, append, expandText, expandTextArea, indent, spawn, export, cursor, getMore, getAll, tools, startAutoUpdate, stopAutoUpdate, expandAll, collapseAll } Iterator<DBObject> iterator; DBCursor dbcursor; TabbedDiv tabbedDiv; Thread updateThread; String updateType; int updateInterval; int updateCount; boolean running; DbJob job; public DocView(String id, String label, DbJob job, Object root) { try { xmlLoad(Resource.getXmlDir(), Resource.File.docView, null); } catch (Exception ex) { getLogger().log(Level.SEVERE, null, ex); } setEnumBinding(Item.values(), this); setId(id); setLabel(label); this.job = job; setStringFieldValue(Item.tabTitle, label); getTree().label = root.toString(); if (job != null) { if (job.getButton() != null) { getComponentBoundUnit(Item.spawn).enabled = true; } ((MenuItem) getBoundUnit(Item.refresh)).enabled = true; ((MenuItem) getBoundUnit(Item.append)).enabled = true; ((MenuItem) getBoundUnit(Item.startAutoUpdate)).enabled = true; } } /** * create a doc view with static document * * @param id * @param label * @param job * @param root * @param doc */ public DocView(String id, String label, DbJob job, Object root, DBObject doc) { this(id, label, job, root); if (doc != null) { addDocument(doc, job, true); } } /** * create a doc view with an iterator or a cursor * * @param id * @param label * @param job * @param root * @param iterator */ public DocView(String id, String label, DbJob job, Object root, Iterator<DBObject> iterator) { this(id, label, job, root); if (iterator instanceof DBCursor) { this.dbcursor = (DBCursor) iterator; this.iterator = dbcursor; ((MenuItem) getBoundUnit(Item.refresh)).enabled = true; ((MenuItem) getBoundUnit(Item.startAutoUpdate)).enabled = true; } else { this.iterator = iterator; } getMore(null); } // /** // * create a doc view from a reusable command with existing result // * @param id // * @param label // * @param job // * @param db // * @param cmd // * @param result // */ // public DocView(String id, String label, DbJob job, DB db, DBObject cmd, DBObject result) { // this(id, label, job, db.getName() + ": " + cmd, result); // this.db = db; // this.cmd = cmd; // // ((MenuItem) getBoundUnit(Item.startAutoUpdate)).enabled = true; // ((MenuItem) getBoundUnit(Item.refresh)).enabled = true; // ((MenuItem) getBoundUnit(Item.append)).enabled = true; // if (result == null) { // refresh(); // } // } // // /** // * create a doc view to run a simple command against a database // * @param id // * @param label // * @param col // * @param panel // * @param cmdStr // */ // public DocView(String id, String label, DB db, String cmdStr) { // this(id, label, null, db, new BasicDBObject(cmdStr, 1), null); // } // // /** // * create a doc view to run a simple command against a collection // * @param id // * @param label // * @param col // * @param cmdStr // */ // public DocView(String id, String label, DBCollection col, String cmdStr) { // this(id, label, null, col.getDB(), new BasicDBObject(cmdStr, col.getName()), null); // } Tree getTree() { return (Tree) getBoundUnit(Item.docTree); } public DBCursor getDBCursor() { return dbcursor; } public void close(ButtonBase button) { if (dbcursor != null) { dbcursor.close(); dbcursor = null; } tabbedDiv.removeTab(this); } void addToTabbedDiv() { tabbedDiv = UMongo.instance.getTabbedResult(); tabbedDiv.addTab(this, true); getTree().expandNode(getTree().getTreeNode()); } public void actionPerformed(Enum enm, XmlComponentUnit unit, Object src) { } public void export(ButtonBase button) throws IOException { // export should be run in thread, to prevent concurrent mods ExportDialog dia = UMongo.instance.getGlobalStore().getExportDialog(); if (!dia.show()) { return; } final DocumentSerializer ds = dia.getDocumentSerializer(); try { DefaultMutableTreeNode root = getTree().getTreeNode(); for (int i = 0; i < root.getChildCount(); ++i) { DefaultMutableTreeNode child = (DefaultMutableTreeNode) root.getChildAt(i); Object obj = child.getUserObject(); DBObject doc = null; if (obj instanceof DBObject) { doc = (DBObject) obj; } else if (obj instanceof TreeNodeDocumentField) { doc = (DBObject) ((TreeNodeDocumentField) obj).getValue(); } else if (obj instanceof TreeNodeDocument) { doc = ((TreeNodeDocument) obj).getDBObject(); } if (doc != null) { ds.writeObject(doc); } } } finally { ds.close(); } } public Component getTabComponent() { return getComponentBoundUnit("tabDiv").getComponent(); } public void startAutoUpdate(ButtonBase button) { AutoUpdateDialog dia = UMongo.instance.getGlobalStore().getAutoUpdateDialog(); if (!dia.show()) { return; } if (updateThread != null) { stopAutoUpdate(null); } updateThread = new Thread(this); updateType = dia.getComponentStringFieldValue(AutoUpdateDialog.Item.autoType); updateInterval = dia.getComponentIntFieldValue(AutoUpdateDialog.Item.autoInterval); updateCount = dia.getComponentIntFieldValue(AutoUpdateDialog.Item.autoCount); running = true; updateThread.start(); getComponentBoundUnit(Item.stopAutoUpdate).enabled = true; getComponentBoundUnit(Item.stopAutoUpdate).updateComponent(); } public void stopAutoUpdate(ButtonBase button) { running = false; try { updateThread.interrupt(); updateThread.join(); } catch (InterruptedException ex) { } updateThread = null; getComponentBoundUnit(Item.stopAutoUpdate).enabled = false; getComponentBoundUnit(Item.stopAutoUpdate).updateComponent(); } public void run() { int i = 0; while (running) { try { DbJob job = null; if ("Refresh".equals(updateType) || dbcursor != null) { job = getRefreshJob(); } else if ("Append".equals(updateType)) { job = getAppendJob(); } final DbJob fjob = job; SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { fjob.addJob(); } }); try { fjob.join(); } catch (InterruptedException ex) { getLogger().log(Level.WARNING, null, ex); } catch (ExecutionException ex) { getLogger().log(Level.WARNING, null, ex); } if (updateCount > 0 && ++i >= updateCount) { break; } Thread.sleep(updateInterval * 1000); } catch (Exception ex) { getLogger().log(Level.SEVERE, null, ex); } } getLogger().log(Level.INFO, "Ran " + i + " updates"); } public void refresh(ButtonBase button) { getRefreshJob().addJob(); } public DbJob getRefreshJob() { if (dbcursor != null) { return getUpdateCursorJob(); } else { return getRefreshJob(false); } } public void append(ButtonBase button) { getAppendJob().addJob(); } public DbJob getAppendJob() { return getRefreshJob(true); } public void spawn(ButtonBase button) { if (job != null) { job.spawnDialog(); } } public void expandText(ButtonBase button) throws IOException { FormDialog dia = (FormDialog) button.getDialog(); DefaultMutableTreeNode root = getTree().getTreeNode(); DefaultMutableTreeNode select = getTree().getSelectionNode(); String txt = ""; if (select == null || select == root) { txt = getTextAllNodes(false); } else { txt = getTextForNode(select.getUserObject(), false); } setStringFieldValue(Item.expandTextArea, txt); dia.label = this.label; dia.show(); } String getTextAllNodes(boolean indent) throws IOException { StringBuilder b = new StringBuilder(); DefaultMutableTreeNode root = getTree().getTreeNode(); for (int i = 0; i < root.getChildCount(); ++i) { DefaultMutableTreeNode child = (DefaultMutableTreeNode) root.getChildAt(i); Object obj = child.getUserObject(); b.append(getTextForNode(obj, indent)); } return b.toString(); } String getTextForNode(Object obj, boolean indent) throws IOException { DBObject doc = null; if (obj instanceof DBObject) { doc = (DBObject) obj; } else if (obj instanceof TreeNodeDocumentField) { doc = (DBObject) ((TreeNodeDocumentField) obj).getValue(); } else if (obj instanceof TreeNodeDocument) { doc = ((TreeNodeDocument) obj).getDBObject(); } if (doc == null) { return ""; } final DocumentSerializer ds = new DocumentSerializer(DocumentSerializer.Format.JSON, null); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); ds.setOutputStream(baos); try { ds.writeObject(doc); } finally { ds.close(); } String txt = new String(baos.toByteArray()); if (indent) { Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); JsonParser jp = new JsonParser(); JsonElement je = jp.parse(txt); txt = gson.toJson(je); // add newline in case there are following docs txt += "\n"; } return txt; } public void indent(ButtonBase button) throws IOException { DefaultMutableTreeNode root = getTree().getTreeNode(); DefaultMutableTreeNode select = getTree().getSelectionNode(); String txt = ""; if (select == null || select == root) { txt = getTextAllNodes(true); } else { txt = getTextForNode(select.getUserObject(), true); } setComponentStringFieldValue(Item.expandTextArea, txt); } public DbJob getRefreshJob(final boolean append) { if (job == null) { return null; } DbJob newJob = new DbJob() { BasicDBObject result; @Override public Object doRun() throws Exception { result = (BasicDBObject) job.doRun(); return null; } @Override public String getNS() { return job.getNS(); } @Override public String getShortName() { return job.getShortName(); } @Override public void wrapUp(Object res) { super.wrapUp(res); if (res == null && result != null) { if (!append) { getTree().removeAllChildren(); } addDocument(result, this); getTree().structureComponent(); // result of command should be fully expanded getTree().expandAll(); // panel info may need to be refreshed if (job.getPanel() != null) { job.getPanel().refresh(); } } } }; return newJob; } public void getMore(final int max) { new DbJob() { @Override public Object doRun() throws IOException { int i = 0; while (iterator.hasNext() && (i++ < max || max <= 0)) { DBObject obj = iterator.next(); if (obj == null) { break; } addDocument(obj, null); } return null; } @Override public String getNS() { if (dbcursor != null) { return dbcursor.getCollection().getFullName(); } return getLabel(); } @Override public String getShortName() { return "Find"; } @Override public void wrapUp(Object res) { super.wrapUp(res); if (res == null) { // res should be null // should have a cursor id now if (dbcursor != null) { BasicDBObject desc = getDescription(DocView.this.job.getRoot(dbcursor)); getTree().label = desc.toString(); } getTree().structureComponent(); getTree().expandNode(getTree().getTreeNode()); DocView.this.updateButtons(); } } }.addJob(); } public void getMore(ButtonBase button) { getMore(UMongo.instance.getPreferences().getGetMoreSize()); } public void getAll(ButtonBase button) { getMore(0); } public DefaultMutableTreeNode getSelectedNode() { TreePath path = getTree().getSelectionPath(); if (path == null || path.getPathCount() < 2) { return null; } return (DefaultMutableTreeNode) path.getLastPathComponent(); } public DBObject getSelectedDocument() { TreePath path = getTree().getSelectionPath(); if (path == null || path.getPathCount() < 2) { return null; } DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getPathComponent(1); Object obj = node.getUserObject(); if (obj instanceof TreeNodeDocumentField) { return (DBObject) ((TreeNodeDocumentField) obj).getValue(); } else if (obj instanceof DBObject) { return (DBObject) obj; } else if (obj instanceof TreeNodeDocument) { return ((TreeNodeDocument) obj).getDBObject(); } return null; } String getSelectedDocumentPath() { TreePath path = getTree().getSelectionPath(); String pathStr = ""; if (path.getPathCount() < 2) { return null; } for (int i = 2; i < path.getPathCount(); ++i) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getPathComponent(i); String key = ((TreeNodeDocumentField) node.getUserObject()).getKey(); pathStr += "." + key; } return pathStr.substring(1); } public DbJob getUpdateCursorJob() { if (dbcursor == null) { return null; } // final int count = getTree().getChildren().size(); DbJob newJob = new DbJob() { @Override public Object doRun() throws Exception { dbcursor = (DBCursor) job.doRun(); return null; } @Override public String getNS() { return job.getNS(); } @Override public String getShortName() { return job.getShortName(); } @Override public void wrapUp(Object res) { super.wrapUp(res); if (res == null) { iterator = dbcursor; getTree().removeAllChildren(); getMore(null); } } }; return newJob; } private void updateButtons() { boolean canGetMore = false; if (dbcursor != null) { // can always get more from tailable cursor canGetMore = iterator.hasNext() || dbcursor.getCursorId() > 0; } else { canGetMore = iterator.hasNext(); } getComponentBoundUnit(Item.getMore).enabled = canGetMore; getComponentBoundUnit(Item.getMore).updateComponent(); getComponentBoundUnit(Item.getAll).enabled = canGetMore; getComponentBoundUnit(Item.getAll).updateComponent(); } public void addDocument(DBObject doc, DbJob job) { addDocument(doc, job, false); } public void addDocument(DBObject doc, DbJob job, boolean expand) { TreeNodeLabel node = new TreeNodeDocument(doc, job); getTree().addChild(node); if (expand) { getTree().expandNode(node); } } // protected void appendDoc(DBObject doc) { // TreeNodeLabel node = new TreeNodeLabel(); // node.forceTreeNode(MongoUtils.dbObjectToTreeNode(doc)); // getTree().addChild(node); // } public void collapseAll(ButtonBase button) { getTree().collapseAll(); // need to reexpand root getTree().expandNode(getTree().getTreeNode()); } public void expandAll(ButtonBase button) { getTree().expandAll(); } }