/* * Copyright (C) 2016 Jindra * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.jpexs.decompiler.flash.gui.generictageditors; import com.jpexs.decompiler.flash.amf.amf3.Amf3Value; import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter; import com.jpexs.decompiler.flash.gui.AppStrings; import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.gui.editor.LineMarkedEditorPane; import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer; import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3ParseException; import com.jpexs.helpers.Helper; import com.jpexs.helpers.ReflectionTools; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.io.IOException; import java.lang.reflect.Field; import java.util.Timer; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; /** * * @author JPEXS */ public class Amf3ValueEditor extends JPanel implements GenericTagEditor, FullSized { private LineMarkedEditorPane editor = new LineMarkedEditorPane(); private final Object obj; private final Field field; private final int index; private final Class<?> type; private String fieldName; private Amf3Value value; private final JLabel errorLabel = new JLabel(); private Timer hideErrorTimer; private final int TIMEOUT = 5000; @Override public void added() { String s = editor.getText(); editor.setContentType("text/javascript"); editor.setText(s); editor.setCaretPosition(0); //Dimension csize = getPreferredSize(); //System.out.println("max=" + getMaximumSize()); //csize.height = 300; //Dimension editorSize = editor.getPreferredSize(); //setPreferredSize(csize); } @Override public BaselineResizeBehavior getBaselineResizeBehavior() { return Component.BaselineResizeBehavior.CONSTANT_ASCENT; } @Override public int getBaseline(int width, int height) { return 0; } public Amf3ValueEditor(String fieldName, Object obj, Field field, int index, Class<?> type) { this.obj = obj; this.field = field; this.index = index; this.type = type; this.fieldName = fieldName; setPreferredSize(new Dimension(800, 200)); setLayout(new BorderLayout()); JPanel titlePanel = new JPanel(new BorderLayout()) { @Override public Insets getInsets() { Insets is = super.getInsets(); is.left = 5; return is; } }; JLabel titleLabel = new JLabel(AppStrings.translate("generic.editor.amf3.title")); titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD)); titlePanel.add(titleLabel, BorderLayout.CENTER); JButton helpButton = new JButton(View.getIcon("about16")); helpButton.setFocusable(false); JTextArea txthelp = new JTextArea(); txthelp.setFont(new Font("monospaced", Font.PLAIN, 12)); final String SCALAR_SAMPLES = " * undefined, null, false, true\n" + " * integer : -5, 5, 29\n" + " * double: 5.5, 1.27, -187.568\n" + " * String: \"hello\", \"escape new\\nline\"\n"; final String NONSCALAR_SAMPLES = " * XMLDocument: {\"type\":\"XMLDocument\", value: \"<foo></foo>\"}\n" + " * Date: {\"type\":\"Date\", \"value\": \"2016-07-17 14:51:42.584\"}\n" + " * Array: {\"type\":\"Array\", \"denseValues\": [27,58,99], \"associativeValues\": {\"key1\":5, \"key2\": -4.6 }}\n" + " * Object: {\"type\":\"Object\",\"className\":\"\",\"dynamic\":true,\n" + " \"sealedMembers\": {\"smemberA\": \"abc\", \"smemberB\":987.5},\n" + " \"dynamicMembers\": {\"member1\": 5, \"member2\": \"aaa\"}\n" + " }\n" + " * XML: {\"type\":\"XML\", value: \"<foo></foo>\"}\n" + " * ByteArray: {\"type\":\"ByteArray\", \"value\":\"B0312F\"}\n" + " * Vector: {\"type\": \"Vector\", \"fixed\":false, \"subtype\":\"int\", \"values\": [8, 4, 6]}\n" + " * Dictionary: {\"type\": \"Dictionary\", \"weakKeys\":false, \"entries\": { \"dkey1\" : \"val1\", \"dkey2\": 56 }}\n"; final String REFERENCE_SAMPLE = " {\"type\": \"Vector\", \"fixed\":false, \"subtype\":\"\",\n" + " \"values\": [\n" + " {\"type\":\"Date\", id=\"mydate\",\"value\": \"2016-07-17 14:51:42.584\"}\n" + " #mydate,\n" + " #mydate,\n" + " {\"type\":\"Date\", \"value\": \"2016-07-26 18:12:22.188\"}\n" + " ]}"; txthelp.setText(AppStrings.translate("generic.editor.amf3.help").replace("%scalar_samples%", SCALAR_SAMPLES).replace("%nonscalar_samples%", NONSCALAR_SAMPLES).replace("%reference_sample%", REFERENCE_SAMPLE)); txthelp.setEditable(false); helpButton.addActionListener((ActionEvent e) -> { View.showMessageDialog(null, txthelp); }); titlePanel.add(helpButton, BorderLayout.EAST); add(titlePanel, BorderLayout.NORTH); add(new JScrollPane(editor), BorderLayout.CENTER); add(errorLabel, BorderLayout.SOUTH); errorLabel.setBackground(Color.red); errorLabel.setForeground(Color.white); errorLabel.setOpaque(true); reset(); } @Override public void reset() { try { value = (Amf3Value) ReflectionTools.getValue(obj, field, index); if (value == null || value.getValue() == null) { editor.setText(""); } else { String stringVal = Amf3Exporter.amfToString(value.getValue()); editor.setText(stringVal); } } catch (IllegalArgumentException | IllegalAccessException ex) { // ignore } } @Override public void save() { try { Object val = getChangedValue(); ReflectionTools.setValue(obj, field, index, val); value = (Amf3Value) val; } catch (IllegalAccessException ex) { //ignore } } @Override public void addChangeListener(final ChangeListener l) { final GenericTagEditor t = this; editor.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { l.change(t); } }); } @Override public void validateValue() { Amf3Importer importer = new Amf3Importer(); String textVal = editor.getText(); try { if (!textVal.trim().isEmpty()) { importer.stringToAmf(textVal); } } catch (IOException | Amf3ParseException ex) { if (ex instanceof Amf3ParseException) { Amf3ParseException ape = (Amf3ParseException) ex; if (ape.line > 0) { editor.gotoLine((int) ape.line); } } final CaretListener cl = new CaretListener() { @Override public void caretUpdate(CaretEvent e) { errorLabel.setVisible(false); editor.removeCaretListener(this); } }; editor.addCaretListener(cl); errorLabel.setText("<html>" + AppStrings.translate("error") + ":" + ex.getMessage() + "</html>"); errorLabel.setVisible(true); throw new IllegalArgumentException("Invalid AMF value", ex); } } @Override public Object getChangedValue() { Amf3Importer importer = new Amf3Importer(); String textVal = editor.getText(); try { return textVal.trim().isEmpty() ? null : new Amf3Value(importer.stringToAmf(textVal)); } catch (IOException | Amf3ParseException ex) { return value; } } @Override public String getFieldName() { return fieldName; } @Override public Field getField() { return field; } @Override public String getReadOnlyValue() { return Helper.escapeHTML(getChangedValue().toString()); } }