package com.gpl.rpg.atcontentstudio.ui; import com.gpl.rpg.atcontentstudio.ATContentStudio; import com.gpl.rpg.atcontentstudio.Notification; import com.gpl.rpg.atcontentstudio.model.GameDataElement; import com.gpl.rpg.atcontentstudio.model.GameSource; import com.gpl.rpg.atcontentstudio.model.Project; import com.gpl.rpg.atcontentstudio.model.gamedata.*; import com.jidesoft.swing.JideBoxLayout; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListDataListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; public class JSONImportWizard extends JDialog { private static final long serialVersionUID = 661234868711700156L; public static enum DataType { none, actorCondition, dialogue, droplist, item, itemCategory, npc, quest } Project proj; JPanel pane; JLabel message; @SuppressWarnings("rawtypes") JComboBox dataTypeCombo; JRadioButton importFromFile; JRadioButton importPasted; JPanel fileSelectionPane; JTextField jsonFileName; JButton browse; RSyntaxTextArea jsonPasteArea; JScrollPane scroller; @SuppressWarnings("rawtypes") JList createdPreview; JPanel buttonPane; JButton ok, cancel; ActionListener okListener, cancelListener; @SuppressWarnings({"rawtypes", "unchecked"}) public JSONImportWizard(Project proj) { super(ATContentStudio.frame); setTitle("Import data from JSON"); this.proj = proj; pane = new JPanel(); pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS, 6)); pane.add(new JLabel("Import data in JSON format."), JideBoxLayout.FIX); message = new JLabel(); dataTypeCombo = new JComboBox(new DataTypeComboModel()); dataTypeCombo.setRenderer(new DataTypeComboCellRenderer()); dataTypeCombo.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { checkEnableNext(); } } }); importPasted = new JRadioButton("Paste JSON text"); importFromFile = new JRadioButton("Select .json file"); importPasted.setSelected(true); ButtonGroup radioGroup = new ButtonGroup(); radioGroup.add(importPasted); radioGroup.add(importFromFile); importPasted.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (importPasted.isSelected()) { scroller.setVisible(true); fileSelectionPane.setVisible(false); pane.revalidate(); pane.repaint(); } } }); importFromFile.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (importFromFile.isSelected()) { scroller.setVisible(false); fileSelectionPane.setVisible(true); pane.revalidate(); pane.repaint(); } } }); jsonFileName = new JTextField(); jsonFileName.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { checkEnableNext(); } @Override public void insertUpdate(DocumentEvent e) { checkEnableNext(); } @Override public void changedUpdate(DocumentEvent e) { checkEnableNext(); } }); browse = new JButton("Browse"); browse.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser jfc = new JFileChooser() { private static final long serialVersionUID = -3001082967957619011L; @Override public boolean accept(File f) { if (f.isDirectory() || f.getName().endsWith(".json") || f.getName().endsWith(".JSON")) { return super.accept(f); } else { return false; } } }; jfc.setMultiSelectionEnabled(false); int result = jfc.showOpenDialog(ATContentStudio.frame); if (result == JFileChooser.APPROVE_OPTION) { jsonFileName.setText(jfc.getSelectedFile().getAbsolutePath()); checkEnableNext(); } } }); fileSelectionPane = new JPanel(); fileSelectionPane.setLayout(new JideBoxLayout(fileSelectionPane, JideBoxLayout.LINE_AXIS, 6)); fileSelectionPane.add(new JLabel("JSON File: "), JideBoxLayout.FIX); fileSelectionPane.add(jsonFileName, JideBoxLayout.VARY); fileSelectionPane.add(browse, JideBoxLayout.FIX); jsonPasteArea = new RSyntaxTextArea(); jsonPasteArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JSON); jsonPasteArea.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { checkEnableNext(); } @Override public void insertUpdate(DocumentEvent e) { checkEnableNext(); } @Override public void changedUpdate(DocumentEvent e) { checkEnableNext(); } }); buttonPane = new JPanel(); buttonPane.setLayout(new JideBoxLayout(buttonPane, JideBoxLayout.LINE_AXIS, 6)); buttonPane.add(new JPanel(), JideBoxLayout.VARY); cancel = new JButton("Cancel"); buttonPane.add(cancel, JideBoxLayout.FIX); ok = new JButton("Next"); buttonPane.add(ok, JideBoxLayout.FIX); createdPreview = new JList(new GDEListModel(new ArrayList())); showFirstScreen(); getContentPane().setLayout(new BorderLayout()); getContentPane().add(pane, BorderLayout.CENTER); setMinimumSize(new Dimension(450, 350)); pack(); Dimension sdim = Toolkit.getDefaultToolkit().getScreenSize(); Dimension wdim = getSize(); setLocation((sdim.width - wdim.width) / 2, (sdim.height - wdim.height) / 2); } private void showFirstScreen() { pane.removeAll(); message.setText("Select a data type & paste your JSON data below:"); pane.add(message, JideBoxLayout.FIX); pane.add(dataTypeCombo, JideBoxLayout.FIX); pane.add(importPasted, JideBoxLayout.FIX); pane.add(importFromFile, JideBoxLayout.FIX); pane.add(fileSelectionPane, JideBoxLayout.FIX); scroller = new JScrollPane(jsonPasteArea); scroller.getVerticalScrollBar().setUnitIncrement(16); JPanel scrollHolder = new JPanel(); scrollHolder.setLayout(new BorderLayout()); scrollHolder.add(scroller, BorderLayout.CENTER); pane.add(scrollHolder, JideBoxLayout.VARY); pane.add(buttonPane, JideBoxLayout.FIX); ok.setText("Next"); ok.setEnabled(jsonPasteArea.getText() != null && jsonPasteArea.getText().length() > 0 && dataTypeCombo.getSelectedItem() != null && dataTypeCombo.getSelectedItem() != DataType.none); ok.removeActionListener(okListener); okListener = new ActionListener() { @SuppressWarnings({"unchecked", "rawtypes"}) @Override public void actionPerformed(ActionEvent e) { List errors = new ArrayList(); List warnings = new ArrayList(); List created = new ArrayList(); Object jsonParserOutput = null; try { if (importPasted.isSelected()) { jsonParserOutput = new JSONParser().parse(jsonPasteArea.getText()); } else if (importFromFile.isSelected()) { jsonParserOutput = new JSONParser().parse(new FileReader(new File(jsonFileName.getText()))); } } catch (ParseException e1) { errors.add("Invalid JSON content: " + e1.getMessage()); } catch (FileNotFoundException e1) { errors.add("Unable to access file: " + e1.getMessage()); } catch (IOException e1) { errors.add("Error while accessing file: " + e1.getMessage()); } if (jsonParserOutput != null) { List jsonObjects = null; if (jsonParserOutput instanceof List) { jsonObjects = (List) jsonParserOutput; } else if (jsonParserOutput instanceof Map) { jsonObjects = new ArrayList(); jsonObjects.add((Map) jsonParserOutput); } else { errors.add("Invalid JSON content: neither an array nor an object."); } if (jsonObjects != null) { JSONElement node; JSONElement existingNode; int i = 0; for (Map jsonObject : jsonObjects) { switch ((DataType) dataTypeCombo.getSelectedItem()) { case actorCondition: node = ActorCondition.fromJson(jsonObject); existingNode = proj.getActorCondition(node.id); break; case item: node = Item.fromJson(jsonObject); existingNode = proj.getItem(node.id); break; case npc: node = NPC.fromJson(jsonObject); existingNode = proj.getNPC(node.id); break; case dialogue: node = Dialogue.fromJson(jsonObject); existingNode = proj.getDialogue(node.id); break; case droplist: node = Droplist.fromJson(jsonObject); existingNode = proj.getDroplist(node.id); break; case itemCategory: node = ItemCategory.fromJson(jsonObject); existingNode = proj.getItemCategory(node.id); break; case quest: node = Quest.fromJson(jsonObject); existingNode = proj.getQuest(node.id); break; default: return; } i++; if (node instanceof JSONElement) { node.parse(jsonObject); created.add(node); if (existingNode != null) { if (existingNode.getDataType() == GameSource.Type.created) { errors.add("An item with id " + node.id + " is already created in this project."); } else if (existingNode.getDataType() == GameSource.Type.altered) { errors.add("An item with id " + node.id + " is already altered in this project."); } else { node.jsonFile = existingNode.jsonFile; warnings.add("An item with id " + node.id + " exists in the used game source. This one will be inserted as \"altered\""); } } } else { warnings.add("Failed to load element #" + i); } } } } if (errors.isEmpty() && warnings.isEmpty()) { showImportPreviewScreen(created); } else if (!errors.isEmpty()) { showErrorScreen(errors); } else { showWarningScreen(warnings, created); } } }; ok.addActionListener(okListener); cancel.setText("Cancel"); cancel.removeActionListener(cancelListener); cancelListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JSONImportWizard.this.setVisible(false); JSONImportWizard.this.dispose(); } }; cancel.addActionListener(cancelListener); if (importPasted.isSelected()) { scroller.setVisible(true); fileSelectionPane.setVisible(false); pane.revalidate(); pane.repaint(); } else if (importFromFile.isSelected()) { scroller.setVisible(false); fileSelectionPane.setVisible(true); pane.revalidate(); pane.repaint(); } pane.revalidate(); pane.repaint(); } private void checkEnableNext() { if (dataTypeCombo.getSelectedItem() != null && dataTypeCombo.getSelectedItem() != DataType.none) { if (importPasted.isSelected()) { ok.setEnabled(jsonPasteArea.getText() != null && jsonPasteArea.getText().length() > 0); } else if (importFromFile.isSelected()) { ok.setEnabled(jsonFileName.getText() != null && jsonFileName.getText().length() > 0 && new File(jsonFileName.getText()).exists() && !(new File(jsonFileName.getText()).isDirectory())); } } } @SuppressWarnings("unchecked") private void showImportPreviewScreen(final List created) { pane.removeAll(); message.setText("The following data has been found. Click \"Ok\" to confirm."); pane.add(message, JideBoxLayout.FIX); createdPreview.setModel(new GDEListModel(created)); createdPreview.setCellRenderer(new GDERenderer(false)); pane.add(new JScrollPane(createdPreview), JideBoxLayout.FIX); pane.add(new JPanel(), JideBoxLayout.VARY); pane.add(buttonPane, JideBoxLayout.FIX); ok.setText("Ok"); ok.removeActionListener(okListener); okListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { proj.createElements(created); JSONElement lastNode = created.get(created.size() - 1); if (lastNode != null) { // lastNode.save(); ATContentStudio.frame.selectInTree(lastNode); } JSONImportWizard.this.setVisible(false); JSONImportWizard.this.dispose(); } }; ok.addActionListener(okListener); cancel.setText("Back"); cancel.removeActionListener(cancelListener); cancelListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { showFirstScreen(); } }; cancel.addActionListener(cancelListener); pane.revalidate(); pane.repaint(); } @SuppressWarnings("unchecked") private void showErrorScreen(List errors) { pane.removeAll(); message.setText("Failed to import. The following error(s) have been encountered:"); pane.add(message, JideBoxLayout.FIX); createdPreview.setModel(new GDEListModel(errors)); createdPreview.setCellRenderer(new ErrorRenderer()); pane.add(new JScrollPane(createdPreview), JideBoxLayout.VARY); pane.add(buttonPane, JideBoxLayout.FIX); cancel.setText("Back"); cancel.removeActionListener(cancelListener); cancelListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { showFirstScreen(); } }; cancel.addActionListener(cancelListener); ok.setText("Close"); ok.removeActionListener(okListener); okListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JSONImportWizard.this.setVisible(false); JSONImportWizard.this.dispose(); } }; ok.addActionListener(okListener); pane.revalidate(); pane.repaint(); } @SuppressWarnings("unchecked") private void showWarningScreen(List warnings, final List created) { pane.removeAll(); message.setText("The following warnings(s) were raised while importing:"); pane.add(message, JideBoxLayout.FIX); createdPreview.setModel(new GDEListModel(warnings)); createdPreview.setCellRenderer(new WarningRenderer()); pane.add(new JScrollPane(createdPreview), JideBoxLayout.VARY); pane.add(buttonPane, JideBoxLayout.FIX); ok.setText("Continue anyway"); ok.removeActionListener(okListener); okListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { showImportPreviewScreen(created); } }; ok.addActionListener(okListener); cancel.setText("Close"); cancel.removeActionListener(cancelListener); cancelListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JSONImportWizard.this.setVisible(false); JSONImportWizard.this.dispose(); } }; cancel.addActionListener(cancelListener); pane.revalidate(); pane.repaint(); } public class GDERenderer extends DefaultListCellRenderer { private static final long serialVersionUID = 6819681566800482793L; private boolean includeType; public GDERenderer(boolean includeType) { super(); this.includeType = includeType; } @Override public Component getListCellRendererComponent(@SuppressWarnings("rawtypes") JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value == null) { label.setText("none"); } else { if (includeType && ((GameDataElement) value).getDataType() != null) { label.setText(((GameDataElement) value).getDataType().toString() + "/" + ((GameDataElement) value).getDesc()); } else { label.setText(((GameDataElement) value).getDesc()); } switch ((DataType) dataTypeCombo.getSelectedItem()) { case actorCondition: label.setIcon(new ImageIcon(proj.getIcon(((ActorCondition) value).icon_id))); break; case item: label.setIcon(new ImageIcon(proj.getIcon(((Item) value).icon_id))); break; case npc: label.setIcon(new ImageIcon(proj.getIcon(((NPC) value).icon_id))); break; case dialogue: label.setIcon(new ImageIcon(((Dialogue) value).getIcon())); break; case droplist: label.setIcon(new ImageIcon(((Droplist) value).getIcon())); break; case itemCategory: label.setIcon(new ImageIcon(((ItemCategory) value).getIcon())); break; case quest: label.setIcon(new ImageIcon(((Quest) value).getIcon())); break; default: Notification.addError("Unable to find icon for " + ((GameDataElement) value).getDesc()); } } return label; } } public static class ErrorRenderer extends DefaultListCellRenderer { private static final long serialVersionUID = -4265342800284721660L; @Override public Component getListCellRendererComponent(@SuppressWarnings("rawtypes") JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (c instanceof JLabel) { ((JLabel) c).setIcon(NotificationsPane.icons.get(Notification.Type.ERROR)); } return c; } } public static class WarningRenderer extends DefaultListCellRenderer { private static final long serialVersionUID = -3836045237946111606L; @Override public Component getListCellRendererComponent(@SuppressWarnings("rawtypes") JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (c instanceof JLabel) { ((JLabel) c).setIcon(NotificationsPane.icons.get(Notification.Type.WARN)); } return c; } } @SuppressWarnings("rawtypes") public static class GDEListModel implements ListenerCollectionModel { List source; public GDEListModel(List source) { this.source = source; } @Override public Collection getElements() { return source; } List listeners = new CopyOnWriteArrayList(); @Override public List getListeners() { return listeners; } } @SuppressWarnings("rawtypes") public static class DataTypeComboModel implements ComboBoxModel { DataType selected = DataType.none; @Override public int getSize() { return DataType.values().length; } @Override public Object getElementAt(int index) { return DataType.values()[index]; } List listeners = new CopyOnWriteArrayList(); @Override public void addListDataListener(ListDataListener l) { listeners.add(l); } @Override public void removeListDataListener(ListDataListener l) { listeners.remove(l); } @Override public void setSelectedItem(Object anItem) { selected = (DataType) anItem; } @Override public Object getSelectedItem() { return selected; } } public static class DataTypeComboCellRenderer extends DefaultListCellRenderer { private static final long serialVersionUID = 5621373849299980998L; @Override public Component getListCellRendererComponent(@SuppressWarnings("rawtypes") JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (c instanceof JLabel) { ((JLabel) c).setText(dataTypeDesc((DataType) value)); switch ((DataType) value) { case actorCondition: ((JLabel) c).setIcon(new ImageIcon(DefaultIcons.getActorConditionIcon())); break; case dialogue: ((JLabel) c).setIcon(new ImageIcon(DefaultIcons.getDialogueIcon())); break; case droplist: ((JLabel) c).setIcon(new ImageIcon(DefaultIcons.getDroplistIcon())); break; case item: ((JLabel) c).setIcon(new ImageIcon(DefaultIcons.getItemIcon())); break; case itemCategory: ((JLabel) c).setIcon(new ImageIcon(DefaultIcons.getDroplistIcon())); break; case npc: ((JLabel) c).setIcon(new ImageIcon(DefaultIcons.getNPCIcon())); break; case quest: ((JLabel) c).setIcon(new ImageIcon(DefaultIcons.getQuestIcon())); break; default: break; } } return c; } } public static String dataTypeDesc(DataType type) { switch (type) { case actorCondition: return "Actor Condition"; case dialogue: return "Dialogue"; case droplist: return "Droplist"; case item: return "Item"; case itemCategory: return "Item Category"; case npc: return "NPC"; case quest: return "Quest"; default: return "Select below"; } } }