// Copyright (c)2005 Holobloc Inc. All rights reserved.
// Contributed to the OOONEIDA FBench project under the Common Public License.
// Copyright (c) 2007 University of Auckland
// Contributed to the FBench extension of the OOONEIDA Workbench project under the Common Public License
package fbench;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;
import javax.swing.JEditorPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.text.html.HTMLEditorKit;
import fbench.dom.DOMTextModel;
import fbench.dom.DOMTranslationModel;
import fbench.graph.GraphView;
import fbench.table.ElementTableModel;
import fbench.tree.DOMTree;
import org.w3c.dom.Document;
/**
* A panel for navigating graphical and textual views of an IEC 61499 LibraryElement.
*
* @author JHC, DH, JP
*
* @version 20070301/JP - Removed unused code and moved some segment of codes to other classes
* @version 20070131/DH - Found bugs from WD, AS previous works. Fixed the bugs and created extra dialogs for
* Adding, Modifying and Removing various graphical components
* @version 20061131/WD,AS - Project Manager, Dialog for adding,modifying and removing interface elements
* @version 20051109/JHC - All notifications now via DOM Event mechanism.
* @version 20050418/JHC
*/
public class LibraryElementView extends JPanel {
private static final long serialVersionUID = 3395261111185224686L;
private Document document;
// private Document origDocument;
// int mouseX, mouseY;
private GraphView graphView;
private DOMTree tree;
private DOMTextModel textModel;
private JTextArea xmlArea;
private JSplitPane mainPanel;
private JEditorPane srcArea;
private JEditorPane javaArea;
private JTabbedPane tabbedPane;
private ElementTableModel tblmdl;
private Color selectedColor = new Color(119, 190, 255);
private String savePath = "";
// private TranslatorSource translatorSource;
private Vector<DOMTranslationModel> translationModels = new Vector<DOMTranslationModel>();
/** A Hashtable for plugins to store data that is associated with a library element view */
private Hashtable<String, Object> opaqueData = new Hashtable<String, Object>();
public LibraryElementView(Document doc) {
super();
// java.lang.System.out.println("[LibraryElementView] Init");
this.setDocument(doc);
// java.lang.System.out.println("[LibraryElementView] document " + document.hashCode());
// Gsha041 addition to determine if modified
// origDocument = (Document) doc.cloneNode(true); // deep = true
// (recurssive copy)
// this.translatorSource = new TranslatorSource();
// translatorSource.addTranslator(STtoJava.class, "ST", "Java");
initComponents();
// java.lang.System.out.println("[LibraryElementView] End Init");
}
private void initComponents() {
tree = new DOMTree(document);
xmlArea = new JTextArea();
srcArea = new JEditorPane();
javaArea = new JEditorPane();
tblmdl = new ElementTableModel(document);
JTable props = new JTable(tblmdl);
props.setSelectionBackground(selectedColor);
props.setFont(new Font("SansSerif", Font.PLAIN, 11));
JScrollPane propsView = new JScrollPane(props);
propsView.setPreferredSize(new Dimension(150, 100));
JScrollPane treeView = new JScrollPane(tree);
treeView.setPreferredSize(new Dimension(150, 150));
// JPanel rightPanel = new JPanel(new BorderLayout());
// rightPanel.add(treeView, BorderLayout.CENTER);
JSplitPane rightPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, treeView, propsView);
rightPane.setResizeWeight(1.0);
rightPane.setPreferredSize(new Dimension(150, 250));
graphView = new GraphView(document);
JScrollPane graphicView = new JScrollPane(graphView);
graphicView.setPreferredSize(new Dimension(550, 250));
rightPane.setMaximumSize(new Dimension());
JSplitPane topPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, graphicView, rightPane);
topPane.setResizeWeight(1.0);
topPane.setDividerLocation(-1);
topPane.setOneTouchExpandable(true);
tabbedPane = createTabs();
tabbedPane.setPreferredSize(new Dimension(700, 140));
mainPanel = update(topPane, tabbedPane);
mainPanel.setResizeWeight(1.0);
setLayout(new BorderLayout());
add("Center", mainPanel);
tree.setSelectionRow(0);
}
public JTabbedPane createTabs() {
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.setFont(new Font("SansSerif", Font.PLAIN, 10));
xmlArea.setSelectionColor(selectedColor);
xmlArea.setEditable(true);
xmlArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
textModel = new DOMTextModel(xmlArea, document);
// ((EventTarget)this.xmlArea).addEventListener("Modified", this, false);
xmlArea.setCaretPosition(0);
JScrollPane xmlView = new JScrollPane(xmlArea);
xmlView.setPreferredSize(new Dimension(200, 150));
tabbedPane.add("XML", xmlView);
srcArea.setSelectionColor(selectedColor);
srcArea.setEditable(false);
srcArea.setEditorKit(new HTMLEditorKit());
translationModels.add(new DOMTranslationModel(document, srcArea, "DOMtoSRC.xsl", "SRC"));
// TODO Complete DOMtoSRC.xls
JScrollPane srcView = new JScrollPane(srcArea);
srcView.setPreferredSize(new Dimension(400, 150));
tabbedPane.add("Src", srcView);
javaArea.setSelectionColor(selectedColor);
javaArea.setEditable(false);
//javaArea.setEditorKit(new HTMLEditorKit());
translationModels.add(new DOMTranslationModel(document, javaArea, "DOMtoJava.xsl", "Java"));
// TODO Complete DOMtoJava.xls
JScrollPane jView = new JScrollPane(javaArea);
jView.setPreferredSize(new Dimension(400, 150));
tabbedPane.add("Java", jView);
return tabbedPane;
}
/**
* Uses nodeListEqual to determine if the document is the same as it was when it was opened
*/
public boolean modified() {
/*
* // Go through current vs orig DOM model... look 4 changes NodeList currentChildren =
* document.getChildNodes(); NodeList origChildren = origDocument.getChildNodes();
* if(!nodeListEqual(currentChildren, origChildren)) { // System.out.println("nodeList NOT Equal");
* return true; } // System.out.println("nodeListEqual"); return false;
*/
return this.textModel.isModified();
}
public void setModified(boolean mod){
this.textModel.setModified(mod);
}
public void addTab(String title, Component component) {
tabbedPane.addTab(title, component);
tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
}
/** Compares 2 nodelists... returns true if the same... false otherwise */
/*
* private boolean nodeListEqual(NodeList a, NodeList b) { // Different sizes //
* System.out.println(a.toString() + " vs " + b.toString() + " : " + // a.getLength() + " children");
*
* if(a.getLength() != b.getLength()) { // System.out.println("Different length"); return false; } else {
* for(int i = 0; i < a.getLength(); i++) { // System.out.println("FOR " + a.item(i).getNodeName() + " vs " // +
* b.item(i).getNodeName() + " : " + // a.item(i).getChildNodes().getLength() + " children"); // Child(i)
* also has children if(a.item(i).hasChildNodes() != b.item(i).hasChildNodes()) { //
* System.out.println("Different children"); return false; } // Children have Different names
* if(!a.item(i).getNodeName().equals(b.item(i).getNodeName())) { // System.out.println("Different
* names"); return false; } // Children have the same contents if((a.item(i) instanceof Element) &&
* (b.item(i) instanceof Element)) { NamedNodeMap Aattribs = ((Element) a.item(i)).getAttributes();
* NamedNodeMap Battribs = ((Element) b.item(i)).getAttributes(); for(int att = 0; att <
* Aattribs.getLength(); att++) { if(!((String) Aattribs.item(att).getNodeValue()).equals((String)
* Battribs .item(att).getNodeValue())) { // System.out.println("Different element contents: " // +
* Aattribs.item(att).getNodeValue() + " " + // Battribs.item(att).getNodeValue()); //
* System.out.println(((Element)a.item(i)).getAttributes()); //
* System.out.println(((Element)b.item(i)).getAttributes()); return false; } } }
* if(!nodeListEqual(a.item(i).getChildNodes(), b.item(i).getChildNodes())) return false; } } //
* System.out.println("They are the same"); return true; }
*/
public JSplitPane updateTop(JScrollPane a, JPanel b) {
return new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, a, b);
}
public JSplitPane update(JSplitPane a, JTabbedPane b) {
return new JSplitPane(JSplitPane.VERTICAL_SPLIT, a, b);
}
public JTextArea getXmlArea() {
return xmlArea;
}
public GraphView getGraphView() {
return graphView;
}
public GraphView getGraphView(Document doc) {
return graphView = new GraphView(doc);
}
public void setSelectedTreeRow(int row) {
tree.setSelectionRow(row);
}
public int getSelectedTreeRow() {
return tree.getSelectionRows()[0];
}
/*
* public void refresh() { try{ // get edited data from xmlArea into a temporary file called temp.fbt
* FileOutputStream fo1 = new FileOutputStream ("temp.fbt"); PrintStream ps1 = new PrintStream(fo1);
* ps1.print(xmlArea.getText()); //Use the factory to create a DOM parser (a.k.a. a DocumentBuilder) try {
* document = Library.load(new InputSource(new FileInputStream("temp.fbt"))); }
* catch(FileNotFoundException ex) {} NodeList e1 = document.getElementsByTagName("FBType");
* graphView.setElement((Element)(e1.item(0))); } catch(Exception ex){ System.out.println(ex.toString()); };
* }{}
*/
public JEditorPane getJavaArea() {
return javaArea;
}
public Document getDocument() {
return document;
}
public DOMTree getTree() {
return tree;
}
public void saveXML() {
if(!this.modified())
return;
if(document == null) {
JOptionPane.showMessageDialog(this, "The XML document contains errors.", "Save failed.",
JOptionPane.WARNING_MESSAGE);
return;
}
String filename = this.getFilename(true); // WITH extn
String classPath = this.getClassPath();
if(classPath == null)
return;
String contents = this.textModel.getText(true); // getText for save (sets textModel Modified = false)
File directory = new File("src" + File.separator + classPath);
if(!directory.exists())
directory.mkdir();
File f = new File(directory, filename);
String tmpName = "";
File[] files = directory.listFiles();
try {
int bak_ctr = -1;
int this_bak = 0;
for(File tmpFile : files) {
if(!tmpFile.isFile()) continue;
tmpName = tmpFile.getName().toUpperCase();
if(tmpName.contains(filename.toUpperCase() + ".BAK")) {
this_bak = Integer.parseInt(tmpName.substring(tmpName.length() - 2));
if(this_bak > bak_ctr) {
bak_ctr = this_bak;
}
}
}
if(f.exists()) {
String bak_number = "" + (bak_ctr + 1);
if(bak_number.length() == 1)
bak_number = "0" + bak_number;
f.renameTo(new File("src\\" + classPath, filename + ".bak" + bak_number));
f.createNewFile();
}
FileWriter fw = new FileWriter(f);
fw.write(contents);
fw.close();
}
catch(IOException e) {
System.err.println(e);
}
setModified(false);
// Update 'origDocument'
// origDocument = (Document) document.cloneNode(true); // deep = true
// (recurssive copy)
}
public void unload() {
Library.remove(document);
}
public void revert() {
textModel.rewrite();
}
public void reparse() {
// Re-parse from xmlarea
// System.out.println(this.xmlArea.getText());
Document doc = Library.parseXML(this.xmlArea.getText());
if(doc == null) {
JOptionPane.showConfirmDialog(this, "Warning " + this.getName()
+ " new Document == null, document NOT updated...", "Reparse Failed.",
JOptionPane.OK_CANCEL_OPTION);
return;
}
this.setDocument(doc);
// re display
textModel.setDocument(document);
textModel.scrollToLine(0);
textModel.setModified(true); // Special case needed... cause we are 'loading' from a textarea...
tree.setDocument(document);
graphView.setDocument(document);
// graphView.setElement(document.getDocumentElement());
graphView.renewElementView();
tblmdl.setDocument(document);
tblmdl.renewElementView();
}
public void setDocument(Document doc) {
document = doc;
setName(this.getFilename(false));
this.updateTabName(this.getName());
// Add to lib
// Can't get classpaths for systems.
if(!FBench.getSourceType(document).equalsIgnoreCase(".sys")) {
System.out.println(getClassPath() + File.separator + getFilename(true));
Library.putDocument(getClassPath() + File.separator + getFilename(true), doc);
}
// else
// Library.putDocument(getFilename(true), doc);
}
private String getClassPath() {
String classPath = "";
try {
String path =
document.getElementsByTagName("CompilerInfo").item(0).getAttributes()
.getNamedItem("header").getNodeValue();
classPath = FBench.stripCompilerInfo(path);
savePath = "./src/" + classPath;
classPath = classPath.replace('.', File.separatorChar);
}
catch(NullPointerException ex) {
String fullPath = Library.getFilePath(document);
if(fullPath == null)
fullPath = FBench.chooseFolderPath();
if(fullPath == null)
return null;
classPath = FBench.stripPath(fullPath) + "";
savePath = "./src/" + classPath;
}
return classPath;
}
protected String getSavePath() {
return savePath;
}
public String getFilename(boolean withExtn) {
String extname = FBench.getSourceType(document);
String filename = document.getDocumentElement().getAttribute("Name");
// Update title
System.out.println("NewName: " + filename);
if(withExtn)
return filename + extname;
else
return filename;
}
public boolean safeToClose() {
if(!this.modified())
return true;
int result =
JOptionPane.showConfirmDialog(this, "Warning " + this.getName()
+ " has not been saved. \n OK to ignore or cancel to cancel", "UnSaved File.",
JOptionPane.OK_CANCEL_OPTION);
// User doesnt care
if(result == JOptionPane.OK_OPTION)
return true;
// user does! care
return false;
}
public void updateTabName(String newName) {
// TODO: update desktop display name...!!!!!!! (it doesnt refresh to new
// name!)
// ((LibraryElementView)desktop.getSelectedComponent()).refresh();
// ***
// This is handled in new tab panel
// ***
// if( this.getParent() instanceof JTabbedPane )
// {
// JTabbedPane parent = (JTabbedPane)this.getParent();
// Component[] allTabs = parent.getComponents();
// for( int tab = 0; tab<allTabs.length; tab++)
// {
// if( this == allTabs[tab] )
// {
// parent.setTitleAt(tab, newName+" ");
// break;
// }
// }
// }
}
/**
* Store data associated with this library element.
* @param key
* @param data
*/
public void putOpaqueData(String key, Object data){
this.opaqueData.put(key, data);
}
/**
* Retrieve data associated with this library element.
* @param key
* @return
*/
public Object getOpaqueData(String key){
return this.opaqueData.get(key);
}
public DOMTextModel getTextModel(){
return textModel;
}
}