/*
* Copyright (c) 2012, Fromentin Xavier, Schnell Michaël, Dervin Cyrielle, Brabant Quentin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The names of its contributors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Fromentin Xavier, Schnell Michaël, Dervin Cyrielle OR Brabant Quentin
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package kameleon.gui.view ;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;
import kameleon.exception.FileReadingException;
import kameleon.exception.KameleonException;
import kameleon.gui.exception.UnknownKeyException;
import kameleon.gui.language.SwitchLanguage;
import kameleon.gui.model.FileModel;
import kameleon.gui.model.GenerationMessage;
import kameleon.gui.model.GenerationMessage.State;
import kameleon.gui.model.Message;
import kameleon.gui.model.Model;
import kameleon.gui.util.FileConstants;
import kameleon.gui.util.ImageUtility;
import kameleon.gui.util.LanguageConstants;
/**
* View displaying the progress of a file generation process. Displays the names
* of the source files and the target files. Also offers the possibility to re-run
* the generation process.
*
* @author Schnell Michaël
* @version 1.0
*/
class ViewGenerationMessage extends ViewMessage
implements FileConstants, LanguageConstants {
/**
* Needed to serialize this class.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = -1477193510863283257L ;
/**
* Font used by file paths.
*/
private final Font TRUETYPE = new Font(
this.getFont().getName(), Font.TRUETYPE_FONT, this.getFont().getSize()) ;
/**
* Label displaying the title for the source file.
*/
private JLabel convertingTitle ;
/**
* Labels displaying the title for every generated file.
*/
private JLabel[] toTitles ;
/**
* Label displaying the status of the generation process.
*/
private JLabel messageLabel ;
/**
* Button used to trigger the re-run of the generation process.
*/
private JButton fastGeneration ;
/**
* Layout manager used by this component.
*/
private GridBagLayout gridbag ;
/**
* Icons displaying the progress for every generated file.
*/
private JLabel[] progressIcons ;
/**
* Sole constructor.
*
* @param model
* model used by this view
*
* @param message
* model backing the information about the displayed message
*
* @throws FileReadingException
* If a format icon could not be loaded
*
* @throws UnknownKeyException
* if an error occurred while updating the text of this view
*/
public ViewGenerationMessage(Model model, final GenerationMessage message)
throws FileReadingException, UnknownKeyException {
this(new GridBagLayout(), model, message) ;
}// ViewGenerationMessage(Model, GenerationMessage)
/**
* Private constructor.
*
* @param gridbag
* layout manager used by this instance
*
* @param model
* model used by this view
*
* @param message
* model backing the information about the displayed message
*
* @throws FileReadingException
* If a format icon could not be loaded
*
* @throws UnknownKeyException
* if an error occurred while updating the text of this view
*/
private ViewGenerationMessage(GridBagLayout gridbag, Model model,
final GenerationMessage message)
throws FileReadingException, UnknownKeyException {
super(model, message) ;
this.gridbag = gridbag ;
this.setLayout(this.gridbag) ;
this.setBorder(BorderFactory.createTitledBorder(
FileModel.DATE_FORMATTER.format(message.getLastGeneration()))) ;
this.build(message) ;
}// ViewGenerationMessage(GridBagLayout, Model, GenerationMessage)
/**
* Builds the content of this instance.
*
* @param gMessage
* model backing the information about the displayed message
*
* @throws FileReadingException
* If a format icon could not be loaded
*
* @throws UnknownKeyException
* if an error occurred while updating the text of this view
*/
private void build(final GenerationMessage gMessage)
throws FileReadingException, UnknownKeyException {
makeSourceRow(gMessage) ;
makeSeparator() ;
makeTargetRows(gMessage) ;
makeSeparator() ;
makeMessageRow(gMessage) ;
this.reloadLanguage() ;
}// build(GenerationMessage)
/**
* Updates the model backing the information about the displayed message.
*
* @param newMessage
* new content for the model
*
* @throws UnknownKeyException
* if an error occurred while updating the text of this view
*/
@Override
public void updateMessage(final Message newMessage) throws UnknownKeyException {
this.message = newMessage ;
GenerationMessage gMessage = (GenerationMessage) newMessage ;
// Update progress icons
int rowIndex = 0 ;
for(JLabel progressIcon : this.progressIcons) {
try {
this.initProgressIcon(progressIcon, gMessage.getState(rowIndex)) ;
} catch (KameleonException ke) {
this.model.displayDebugInformation(ke) ;
}// try
++rowIndex ;
}// for
// Update message
this.reloadLanguage() ;
// Update generation date
TitledBorder border = (TitledBorder) this.getBorder() ;
border.setTitle(FileModel.DATE_FORMATTER.format(gMessage.getLastGeneration())) ;
}// updateMessage(Message)
/**
* Builds the source row of this instance. Displays the name and format
* of the source file for the matching generation process.
*
* @param gMessage
* model backing the information about the displayed message
*/
private void makeSourceRow(final GenerationMessage gMessage) {
JLabel label ;
GridBagConstraints constraints ;
// -----
this.convertingTitle = new JLabel() ;
constraints = new GridBagConstraints() ;
constraints.gridwidth = 2 ;
constraints.anchor = GridBagConstraints.ABOVE_BASELINE_LEADING ;
constraints.insets = new Insets(2, 2, 2, 2) ;
constraints.weightx = 0.0 ;
constraints.weighty = 0.0 ;
this.add(this.convertingTitle) ;
this.gridbag.setConstraints(this.convertingTitle, constraints) ;
// -----
try {
label = new JLabel(new ImageIcon(
ImageUtility.getImageBytes(String.format(
MINI_ICON, gMessage.getIdFormat())))) ;
label.setToolTipText(this.model.getAnalyzer(
gMessage.getIdFormat()).getFormatName()) ;
constraints = new GridBagConstraints() ;
constraints.gridwidth = 1 ;
constraints.anchor = GridBagConstraints.ABOVE_BASELINE_LEADING ;
constraints.insets = new Insets(2, 2, 2, 2) ;
constraints.weightx = 0.0 ;
constraints.weighty = 0.0 ;
this.add(label) ;
this.gridbag.setConstraints(label, constraints) ;
} catch(KameleonException ke) {
this.model.displayDebugInformation(ke) ;
}// try
// -----
label = new JLabel(gMessage.getPath()) ;
label.setFont(this.TRUETYPE) ;
constraints = new GridBagConstraints() ;
constraints.gridwidth = GridBagConstraints.REMAINDER ;
constraints.anchor = GridBagConstraints.ABOVE_BASELINE_LEADING ;
constraints.insets = new Insets(2, 2, 2, 2) ;
constraints.weightx = 1.0 ;
constraints.weighty = 0.0 ;
this.add(label) ;
this.gridbag.setConstraints(label, constraints) ;
}// makeSourceRow(GenerationMessage)
/**
* Builds the rows for every generated file. Displays the name, format
* and generation state for each target file.
*
* @param gMessage
* model backing the information about the displayed message
*
* @throws FileReadingException
* If a format icon could not be loaded
*/
private void makeTargetRows(final GenerationMessage gMessage)
throws FileReadingException{
String[] paths = gMessage.getTargetPath() ;
String[] formatsIds = gMessage.getTargetFormatId() ;
State[] states = gMessage.getStates() ;
int nRows = paths.length ;
this.toTitles = new JLabel[nRows] ;
this.progressIcons = new JLabel[nRows] ;
for(int row=0; row < nRows; ++row) {
if ((paths[row] != null) && (formatsIds[row] != null)) {
makeTargetRow(row, paths[row], formatsIds[row], states[row]) ;
}// if
}// for
}// makeTargetRows(GenerationMessage)
/**
* Utility function, simply calls
* <pre>makeRow(rowIndex, "To", path, formatId,
* GridBagConstraints.ABOVE_BASELINE_TRAILING,
* this.model.getGenerator(formatId).getFormatName(),
* state) ;
* </pre>
* (the "To" is translated into the correct language.)
*
* @param rowIndex
* index of the generated row
*
* @param path
* path of the target file
*
* @param formatId
* identifier of the format of the target file
*
* @param state
* current state of the generation process for the target file
*
* @throws FileReadingException
* If a format icon could not be loaded
*/
private void makeTargetRow(int rowIndex, String path, String formatId, State state)
throws FileReadingException {
makeRow(rowIndex, "To", path, formatId, //$NON-NLS-1$
GridBagConstraints.ABOVE_BASELINE_TRAILING,
this.model.getGenerator(formatId).getFormatName(),
state) ;
}// makeTargetRow(int, String, String, State)
/**
* Builds a rowa for a generated file. Displays the name, format
* and generation state using the given values.
*
* @param rowIndex
* index of the generated row
*
* @param title
* text displayed in the first column (title of the row)
*
* @param path
* path of the target file
*
* @param formatId
* identifier of the format of the target file
*
* @param firstCellAnchor
* anchor used by this row
*
* @param tooltip
* tool tip displayed when hovering the format icon
*
* @param state
* current state of the generation process for the target file
*
* @throws FileReadingException
* If a format icon could not be loaded
*/
private void makeRow(int rowIndex, String title, String path, String formatId,
int firstCellAnchor, String tooltip, State state)
throws FileReadingException {
JLabel label ;
GridBagConstraints constraints ;
// -----
this.progressIcons[rowIndex] = this.makeProgressIcon(state) ;
// -----
label = new JLabel(title) ;
constraints = new GridBagConstraints() ;
constraints.gridwidth = 1 ;
constraints.anchor = firstCellAnchor ;
constraints.insets = new Insets(2, 2, 2, 2) ;
constraints.weightx = 0.0 ;
constraints.weighty = 0.0 ;
this.add(label) ;
this.gridbag.setConstraints(label, constraints) ;
this.toTitles[rowIndex] = label ;
// -----
try {
label = new JLabel(new ImageIcon(
ImageUtility.getImageBytes(String.format(
MINI_ICON, formatId)))) ;
label.setToolTipText(tooltip) ;
constraints = new GridBagConstraints() ;
constraints.gridwidth = 1 ;
constraints.anchor = GridBagConstraints.ABOVE_BASELINE_LEADING ;
constraints.insets = new Insets(2, 2, 2, 2) ;
constraints.weightx = 0.0 ;
constraints.weighty = 0.0 ;
this.add(label) ;
this.gridbag.setConstraints(label, constraints) ;
} catch(KameleonException ke) {
this.model.displayDebugInformation(ke) ;
}// try
// -----
label = new JLabel(path) ;
label.setFont(this.TRUETYPE) ;
constraints = new GridBagConstraints() ;
constraints.gridwidth = GridBagConstraints.REMAINDER ;
constraints.anchor = GridBagConstraints.ABOVE_BASELINE_LEADING ;
constraints.insets = new Insets(2, 2, 2, 2) ;
constraints.weightx = 1.0 ;
constraints.weighty = 0.0 ;
this.add(label) ;
this.gridbag.setConstraints(label, constraints) ;
}// makeRow(int, String, String, String, int, String, State)
/**
* Builds a message resuming the global state of the generation process
* and a button to trigger a re-run of the generation process.
*
* @param gMessage
* model backing the information about the current generation process
*
* @throws FileReadingException
* If a format icon could not be loaded
*/
private void makeMessageRow(final GenerationMessage gMessage)
throws FileReadingException {
GridBagConstraints constraints ;
makeReGenerateButton(gMessage) ;
this.messageLabel = new JLabel() ;
constraints = new GridBagConstraints() ;
constraints.gridwidth = GridBagConstraints.REMAINDER ;
constraints.anchor = GridBagConstraints.ABOVE_BASELINE_LEADING ;
constraints.weightx = 1.0 ;
constraints.weighty = 0.0 ;
this.add(this.messageLabel) ;
this.gridbag.setConstraints(this.messageLabel, constraints) ;
}// makeMessageRow(GenerationMessage)
/**
* Builds the button used to trigger a re-run of the generation process.
*
* @param gMessage
* model backing the information about the current generation process
*
* @throws FileReadingException
* If a format icon could not be loaded
*/
private void makeReGenerateButton(final GenerationMessage gMessage)
throws FileReadingException {
GridBagConstraints constraints ;
InputStream src = this.getClass().getResourceAsStream(REGENERATE_FILE) ;
Icon refreshIcon = new ImageIcon(
ImageUtility.getImageBytes(new BufferedInputStream(src))) ;
final JButton button = new JButton(refreshIcon) ;
button.setContentAreaFilled(false) ;
button.setBorder(null) ;
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)) ;
final Model fViewModel = this.model ;
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
button.setEnabled(false) ;
fViewModel.fastGeneration(gMessage) ;
}// actionPerformed(ActionEvent)
}) ;
constraints = new GridBagConstraints() ;
constraints.gridwidth = 2 ;
constraints.anchor = GridBagConstraints.ABOVE_BASELINE_LEADING ;
constraints.weightx = 0.0 ;
constraints.weighty = 0.0 ;
this.add(button) ;
this.gridbag.setConstraints(button, constraints) ;
this.fastGeneration = button ;
}// makeReGenerateButton(GenerationMessage)
/**
* Builds an invisible separator row.
*/
private void makeSeparator() {
JPanel separator = new JPanel() ;
GridBagConstraints constraints = new GridBagConstraints() ;
constraints.gridwidth = GridBagConstraints.REMAINDER ;
constraints.fill = GridBagConstraints.BOTH ;
this.add(separator) ;
this.gridbag.setConstraints(separator, constraints) ;
}// makeSeparator()
/**
* Builds a progress icon for a given state.
*
* @param state
* state used to determine the matching progress icon
*
* @return A {@code JLabel} with the matching progress icon
*
* @throws FileReadingException
* If a format icon could not be loaded
*/
private JLabel makeProgressIcon(State state) throws FileReadingException {
JLabel label = null ;
GridBagConstraints constraints ;
label = new JLabel() ;
this.initProgressIcon(label, state) ;
constraints = new GridBagConstraints() ;
constraints.gridwidth = 1 ;
constraints.anchor = GridBagConstraints.ABOVE_BASELINE ;
constraints.insets = new Insets(2, 2, 2, 2) ;
constraints.weightx = 0.0 ;
constraints.weighty = 0.0 ;
this.add(label) ;
this.gridbag.setConstraints(label, constraints) ;
return label ;
}// makeProgressIcon(State)
/**
* Initializes the progress icon of the given label using the
* given state.
*
* @param label
* label that will contain the progress icon
*
* @param state
* state used to determine the matching progress icon
*
* @throws FileReadingException
* If a format icon could not be loaded
*/
private void initProgressIcon(JLabel label, State state)
throws FileReadingException {
String icon = null ;
switch(state) {
case WAITING:
icon = WAITING_FILE ;
break ;
case CONVERTING:
icon = CONVERTING_FILE ;
break ;
case CONVERTED:
icon = CONVERTED_FILE ;
break ;
case ERROR:
icon = ERROR_FILE ;
}// switch
InputStream src = new BufferedInputStream(ViewGenerationMessage.class
.getResourceAsStream(icon)) ;
label.setIcon(new ImageIcon(
ImageUtility.getImageBytes(src))) ;
try {
src.close() ;
} catch(IOException ioe) {
this.model.displayDebugInformation(
new FileReadingException(ioe.getMessage())) ;
}// try
}// initProgressIcon(JLabel, State)
/**
* Enables or disables the regenerate button.
*/
@Override
public void update() {
if (!this.fastGeneration.isEnabled() && this.model.generationFinished()) {
this.fastGeneration.setEnabled(true) ;
}// if
}// update()
/**
* Updates the text to match the currently selected language.
*
* @throws UnknownKeyException
* if an error occurred while updating the text of this view
*/
@Override
public void reloadLanguage() throws UnknownKeyException {
SwitchLanguage sl = SwitchLanguage.getInstance() ;
GenerationMessage gMessage = (GenerationMessage) this.message ;
this.convertingTitle.setText(sl.getText(CONVERTING)) ;
State[] states = gMessage.getStates() ;
int nRows = states.length ;
for(int row=0; row < nRows; ++row) {
this.toTitles[row].setText(sl.getText(TO)) ;
this.progressIcons[row].setToolTipText(
sl.getText(getTextKey(states[row]))) ;
}// for
this.fastGeneration.setText(sl.getText(REGENERATE)) ;
this.messageLabel.setText(this.getMessageText()) ;
}// reloadLanguage()
/**
* Returns the language key used to describe the given state.
*
* @param state
* state whose language key is requested
*
* @return Language key for the given state
*/
private static String getTextKey(State state) {
switch(state) {
case WAITING: return STATE_WAITING ;
case CONVERTING: return STATE_CONVERTING ;
case CONVERTED: return STATE_CONVERTED ;
default: /* case ERROR */
}// switch
return STATE_ERROR ;
}// getTextKey(State)
/**
* Returns a description for the current state of the generation process.
*
* @return Description for the current state of the generation process
*
* @throws UnknownKeyException
* if an error occurred while updating the text of this view
*/
private String getMessageText() throws UnknownKeyException {
SwitchLanguage sl = SwitchLanguage.getInstance() ;
GenerationMessage gMessage = (GenerationMessage) this.message ;
String textKey, filePath = null ;
switch(gMessage.getState()) {
case ANALYZING: {
textKey = GENERATION_ANALYZING_START ;
break ;
}// case
case GENERATING: {
State[] states = gMessage.getStates() ;
if (states[0].equals(State.WAITING)) {
textKey = GENERATION_ANALYZING_SUCCESS ;
} else {
textKey = GENERATION_GENERATING ;
int i = 0, nStates = states.length ;
while((i < nStates) && (filePath == null)) {
if (states[i].equals(State.CONVERTING)) {
filePath = gMessage.getTargetPath()[i] ;
}// if
++i ;
}// while
}// if
break ;
}// case
case PARTIAL_SUCESS: {
textKey = GENERATION_PARTIAL_SUCCESS ;
break ;
}// case
case COMPLETE_SUCESS: {
textKey = GENERATION_SUCCESS ;
break ;
}// clase
default: {/* ERROR */
textKey = GENERATION_ERROR ;
}// default
}// switch
return sl.getText(textKey, filePath) ;
}// getMessageText()
}// class ViewGenerationMessage