/* $Id: ClassGenerationDialog.java 17869 2010-01-12 20:48:01Z linus $
*****************************************************************************
* Copyright (c) 2009 Contributors - see below
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* tfmorris
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 1996-2009 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies. This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason. IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
package org.argouml.uml.generator.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import org.apache.log4j.Logger;
import org.argouml.i18n.Translator;
import org.argouml.model.Model;
import org.argouml.notation.Notation;
import org.argouml.uml.generator.CodeGenerator;
import org.argouml.uml.generator.GeneratorManager;
import org.argouml.uml.generator.Language;
import org.argouml.util.ArgoDialog;
import org.tigris.swidgets.Dialog;
/**
* The dialog that starts the generation of classes.
*/
public class ClassGenerationDialog extends ArgoDialog
implements ActionListener {
private static final String SOURCE_LANGUAGE_TAG = "src_lang";
/**
* Logger.
*/
private static final Logger LOG = Logger
.getLogger(ClassGenerationDialog.class);
private TableModelClassChecks classTableModel;
private boolean isPathInModel;
private List<Language> languages;
private JTable classTable;
private JComboBox outputDirectoryComboBox;
/**
* Used to select the next language column in case the "Select All" button
* is pressed.
*/
private int languageHistory;
/**
* Constructor.
*
* @param nodes The UML elements, typically classifiers, to generate.
*/
public ClassGenerationDialog(List<Object> nodes) {
this(nodes, false);
}
/**
* Constructor.
* <p>
* TODO: Correct?
*
* @param nodes The UML elements, typically classifiers, to generate.
* @param inModel <code>true</code> if the path is in the model.
*/
public ClassGenerationDialog(List<Object> nodes, boolean inModel) {
super(Translator.localize("dialog.title.generate-classes"),
Dialog.OK_CANCEL_OPTION, true);
isPathInModel = inModel;
buildLanguages();
JPanel contentPanel = new JPanel(new BorderLayout(10, 10));
// Class Table
classTableModel = new TableModelClassChecks();
classTableModel.setTarget(nodes);
classTable = new JTable(classTableModel);
classTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
classTable.setShowVerticalLines(false);
setClassTableColumnWidths();
classTable.setPreferredScrollableViewportSize(new Dimension(300, 300));
// Select Buttons
JButton selectAllButton = new JButton();
nameButton(selectAllButton, "button.select-all");
selectAllButton.addActionListener(new ActionListener() {
/*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.
* ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
classTableModel.setAllChecks(true);
classTable.repaint();
}
});
JButton selectNoneButton = new JButton();
nameButton(selectNoneButton, "button.select-none");
selectNoneButton.addActionListener(new ActionListener() {
/*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.
* ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
classTableModel.setAllChecks(false);
classTable.repaint();
}
});
JPanel selectPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
selectPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0));
JPanel selectButtons = new JPanel(new BorderLayout(5, 0));
selectButtons.add(selectAllButton, BorderLayout.CENTER);
selectButtons.add(selectNoneButton, BorderLayout.EAST);
selectPanel.add(selectButtons);
JPanel centerPanel = new JPanel(new BorderLayout(0, 2));
centerPanel.add(new JLabel(Translator
.localize("label.available-classes")), BorderLayout.NORTH);
centerPanel.add(new JScrollPane(classTable), BorderLayout.CENTER);
centerPanel.add(selectPanel, BorderLayout.SOUTH);
contentPanel.add(centerPanel, BorderLayout.CENTER);
// Output Directory
outputDirectoryComboBox =
new JComboBox(getClasspathEntries().toArray());
JButton browseButton = new JButton();
nameButton(browseButton, "button.browse");
browseButton.setText(browseButton.getText() + "...");
browseButton.addActionListener(new ActionListener() {
/*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.
* ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
doBrowse();
}
});
JPanel southPanel = new JPanel(new BorderLayout(0, 2));
if (!inModel) {
outputDirectoryComboBox.setEditable(true);
JPanel outputPanel = new JPanel(new BorderLayout(5, 0));
outputPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder(Translator
.localize("label.output-directory")), BorderFactory
.createEmptyBorder(2, 5, 5, 5)));
outputPanel.add(outputDirectoryComboBox, BorderLayout.CENTER);
outputPanel.add(browseButton, BorderLayout.EAST);
southPanel.add(outputPanel, BorderLayout.NORTH);
}
// Compile Checkbox
// _compileCheckBox = new JCheckBox();
// nameButton(_compileCheckBox, "checkbox.compile-generated-source");
// TODO: Implement the compile feature. For now, disable the checkbox.
// _compileCheckBox.setEnabled(false);
// southPanel.add(_compileCheckBox, BorderLayout.SOUTH);
contentPanel.add(southPanel, BorderLayout.SOUTH);
setContent(contentPanel);
// TODO: Get saved default directory
// outputDirectoryComboBox.getModel().setSelectedItem(savedDir);
}
/*
* @see org.tigris.swidgets.Dialog#nameButtons()
*/
@Override
protected void nameButtons() {
super.nameButtons();
nameButton(getOkButton(), "button.generate");
}
private void setClassTableColumnWidths() {
TableColumn column = null;
Component c = null;
int width = 0;
for (int i = 0; i < classTable.getColumnCount() - 1; ++i) {
column = classTable.getColumnModel().getColumn(i);
width = 30;
JTableHeader header = classTable.getTableHeader();
if (header != null) {
c = header.getDefaultRenderer()
.getTableCellRendererComponent(classTable,
column.getHeaderValue(), false, false, 0, 0);
width = Math.max(c.getPreferredSize().width + 8, width);
}
column.setPreferredWidth(width);
column.setWidth(width);
column.setMinWidth(width);
column.setMaxWidth(width);
}
}
private void buildLanguages() {
languages = new ArrayList<Language>(GeneratorManager
.getInstance().getLanguages());
Collections.sort(languages);
}
private static Collection<String> getClasspathEntries() {
String classpath = System.getProperty("java.class.path");
Collection<String> entries = new TreeSet<String>();
// TODO: What does the output directory have to do with the class path?
// Project p = ProjectManager.getManager().getCurrentProject();
// entries.add(p.getProjectSettings().getGenerationOutputDir());
final String pathSep = System.getProperty("path.separator");
StringTokenizer allEntries = new StringTokenizer(classpath, pathSep);
while (allEntries.hasMoreElements()) {
String entry = allEntries.nextToken();
if (!entry.toLowerCase().endsWith(".jar")
&& !entry.toLowerCase().endsWith(".zip")) {
entries.add(entry);
}
}
return entries;
}
/*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
// Generate Button --------------------------------------
if (e.getSource() == getOkButton()) {
String path = null;
// TODO: Get default output directory from user settings
// Project p = ProjectManager.getManager().getCurrentProject();
// p.getProjectSettings().setGenerationOutputDir(path);
List<String>[] fileNames = new List[languages.size()];
for (int i = 0; i < languages.size(); i++) {
fileNames[i] = new ArrayList<String>();
Language language = languages.get(i);
GeneratorManager genMan = GeneratorManager.getInstance();
CodeGenerator generator = genMan.getGenerator(language);
Set nodes = classTableModel.getChecked(language);
if (!isPathInModel) {
path = ((String) outputDirectoryComboBox.getModel()
.getSelectedItem());
if (path != null) {
path = path.trim();
if (path.length() > 0) {
Collection<String> files = generator.generateFiles(
nodes, path, false);
for (String filename : files) {
fileNames[i].add(path
+ CodeGenerator.FILE_SEPARATOR
+ filename);
}
}
}
} else {
// classify nodes by base path
Map<String, Set<Object>> nodesPerPath =
new HashMap<String, Set<Object>>();
for (Object node : nodes) {
if (!Model.getFacade().isAClassifier(node)) {
continue;
}
path = GeneratorManager.getCodePath(node);
if (path == null) {
Object parent = Model.getFacade()
.getNamespace(node);
while (parent != null) {
path = GeneratorManager.getCodePath(parent);
if (path != null) {
break;
}
parent = Model.getFacade().getNamespace(parent);
}
}
if (path != null) {
final String fileSep = CodeGenerator.FILE_SEPARATOR;
if (path.endsWith(fileSep)) { // remove trailing /
path = path.substring(0, path.length()
- fileSep.length());
}
Set<Object> np = nodesPerPath.get(path);
if (np == null) {
np = new HashSet<Object>();
nodesPerPath.put(path, np);
}
np.add(node);
saveLanguage(node, language);
}
} // end for (all nodes)
// generate the files
for (Map.Entry entry : nodesPerPath.entrySet()) {
String basepath = (String) entry.getKey();
Set nodeColl = (Set) entry.getValue();
// TODO: the last argument (recursive flag) should be a
// selectable option
Collection<String> files = generator.generateFiles(
nodeColl, basepath, false);
for (String filename : files) {
fileNames[i].add(basepath
+ CodeGenerator.FILE_SEPARATOR + filename);
}
}
} // end if (!isPathInModel) .. else
} // end for (all languages)
// TODO: do something with the generated list fileNames,
// for example, show it to the user in a dialog box.
}
}
/**
* Save the source language in the model.
* <p>
* TODO: Support multiple languages now that we have UML 1.4 tagged values.
*
* @param node
* @param language
*/
private void saveLanguage(Object node, Language language) {
Object taggedValue = Model.getFacade().getTaggedValue(node,
SOURCE_LANGUAGE_TAG);
if (taggedValue != null) {
String savedLang = Model.getFacade().getValueOfTag(taggedValue);
if (!language.getName().equals(savedLang)) {
Model.getExtensionMechanismsHelper().setValueOfTag(taggedValue,
language.getName());
}
} else {
taggedValue = Model.getExtensionMechanismsFactory()
.buildTaggedValue(SOURCE_LANGUAGE_TAG, language.getName());
Model.getExtensionMechanismsHelper().addTaggedValue(node,
taggedValue);
}
}
private void doBrowse() {
try {
// Show Filechooser to select OutputDirectory
JFileChooser chooser = new JFileChooser(
(String) outputDirectoryComboBox.getModel()
.getSelectedItem());
if (chooser == null) {
chooser = new JFileChooser();
}
chooser.setFileHidingEnabled(true);
chooser.setMultiSelectionEnabled(false);
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setDialogTitle(Translator
.localize("dialog.generation.chooser.choose-output-dir"));
chooser.showDialog(this, Translator
.localize("dialog.generation.chooser.approve-button-text"));
if (!"".equals(chooser.getSelectedFile().getPath())) {
String path = chooser.getSelectedFile().getPath();
outputDirectoryComboBox.addItem(path);
outputDirectoryComboBox.getModel().setSelectedItem(path);
} // else ignore
} catch (Exception userPressedCancel) {
// TODO: How does the pressed cancel become a java.lang.Exception?
LOG.info("user pressed cancel");
}
}
class TableModelClassChecks extends AbstractTableModel {
/**
* List of all possible UML elements for which to generate code. The
* Object typed objects are actually UML Elements, but we don't have a
* visible type for that.
*/
private List<Object> classes;
/**
* Array of sets of UML elements that the user has selected. One set per
* language. The Object typed objects are actually UML Elements, but we
* don't have a visible type for that.
*/
private Set<Object>[] checked;
/**
* Constructor.
*/
public TableModelClassChecks() {
}
/**
* Set the target.
*
* @param nodes list of classes
*/
public void setTarget(List<Object> nodes) {
classes = nodes;
checked = new Set[getLanguagesCount()];
for (int j = 0; j < getLanguagesCount(); j++) {
// Doesn't really matter what set we use.
checked[j] = new HashSet<Object>();
}
for (Object cls : classes) {
for (int j = 0; j < getLanguagesCount(); j++) {
if (isSupposedToBeGeneratedAsLanguage(languages.get(j), cls)) {
checked[j].add(cls);
} else if ((languages.get(j)).getName().equals(
Notation.getConfiguredNotation()
.getConfigurationValue())) {
checked[j].add(cls);
}
}
}
fireTableStructureChanged();
getOkButton().setEnabled(
classes.size() > 0 && getChecked().size() > 0);
}
private boolean isSupposedToBeGeneratedAsLanguage(Language lang,
Object cls) {
if (lang == null || cls == null) {
return false;
}
Object taggedValue = Model.getFacade().getTaggedValue(cls,
SOURCE_LANGUAGE_TAG);
if (taggedValue == null) {
return false;
}
String savedLang = Model.getFacade().getValueOfTag(taggedValue);
return (lang.getName().equals(savedLang));
}
private int getLanguagesCount() {
if (languages == null) {
return 0;
}
return languages.size();
}
/**
* Return the set of elements which are selected for the given language.
*
* @param lang the language
* @return a set of UML elements
*/
public Set<Object> getChecked(Language lang) {
int index = languages.indexOf(lang);
if (index == -1) {
return Collections.emptySet();
}
return checked[index];
}
/**
* All checked classes.
*
* @return The union of all languages as a {@link Set}.
*/
public Set<Object> getChecked() {
Set<Object> union = new HashSet<Object>();
for (int i = 0; i < getLanguagesCount(); i++) {
union.addAll(checked[i]);
}
return union;
}
// //////////////
// TableModel implementation
/*
* @see javax.swing.table.TableModel#getColumnCount()
*/
public int getColumnCount() {
return 1 + getLanguagesCount();
}
/*
* @see javax.swing.table.TableModel#getColumnName(int)
*/
@Override
public String getColumnName(int c) {
if (c >= 0 && c < getLanguagesCount()) {
return languages.get(c).getName();
} else if (c == getLanguagesCount()) {
return "Class Name";
}
return "XXX";
}
/*
* @see javax.swing.table.TableModel#getColumnClass(int)
*/
public Class getColumnClass(int c) {
if (c >= 0 && c < getLanguagesCount()) {
return Boolean.class;
} else if (c == getLanguagesCount()) {
return String.class;
}
return String.class;
}
/*
* @see javax.swing.table.TableModel#isCellEditable(int, int)
*/
@Override
public boolean isCellEditable(int row, int col) {
Object cls = classes.get(row);
if (col == getLanguagesCount()) {
return false;
}
if (!(Model.getFacade().getName(cls).length() > 0)) {
return false;
}
if (col >= 0 && col < getLanguagesCount()) {
return true;
}
return false;
}
/*
* @see javax.swing.table.TableModel#getRowCount()
*/
public int getRowCount() {
if (classes == null) {
return 0;
}
return classes.size();
}
/*
* @see javax.swing.table.TableModel#getValueAt(int, int)
*/
public Object getValueAt(int row, int col) {
Object cls = classes.get(row);
if (col == getLanguagesCount()) {
String name = Model.getFacade().getName(cls);
if (name.length() > 0) {
return name;
}
return "(anon)";
} else if (col >= 0 && col < getLanguagesCount()) {
if (checked[col].contains(cls)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
} else {
return "CC-r:" + row + " c:" + col;
}
}
/*
* @see javax.swing.table.TableModel#setValueAt( java.lang.Object, int,
* int)
*/
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == getLanguagesCount()) {
return;
}
if (columnIndex >= getColumnCount()) {
return;
}
if (!(aValue instanceof Boolean)) {
return;
}
boolean val = ((Boolean) aValue).booleanValue();
Object cls = classes.get(rowIndex);
if (columnIndex >= 0 && columnIndex < getLanguagesCount()) {
if (val) {
checked[columnIndex].add(cls);
} else {
checked[columnIndex].remove(cls);
}
}
if (val && !getOkButton().isEnabled()) {
getOkButton().setEnabled(true);
} else if (!val && getOkButton().isEnabled()
&& getChecked().size() == 0) {
getOkButton().setEnabled(false);
}
}
/**
* Sets or clears all checkmarks for the (next) language for all
* classes.
*
* @param value If false then all checkmarks are cleared for all
* languages. If true then all are cleared, except for one
* language column, these are all set.
*/
public void setAllChecks(boolean value) {
int rows = getRowCount();
int checks = getLanguagesCount();
if (rows == 0) {
return;
}
for (int i = 0; i < rows; ++i) {
Object cls = classes.get(i);
for (int j = 0; j < checks; ++j) {
if (value && (j == languageHistory)) {
checked[j].add(cls);
} else {
checked[j].remove(cls);
}
}
}
if (value) {
if (++languageHistory >= checks) {
languageHistory = 0;
}
}
getOkButton().setEnabled(value);
}
/**
* The UID.
*/
private static final long serialVersionUID = 6108214254680694765L;
} /* end class TableModelClassChecks */
/**
* The UID.
*/
private static final long serialVersionUID = -8897965616334156746L;
} /* end class ClassGenerationDialog */