ConfigUserInterface.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package com.l2jserver.configurator;
  16. import java.awt.GridBagConstraints;
  17. import java.awt.GridBagLayout;
  18. import java.awt.Insets;
  19. import java.awt.Rectangle;
  20. import java.awt.event.ActionEvent;
  21. import java.awt.event.ActionListener;
  22. import java.io.BufferedWriter;
  23. import java.io.File;
  24. import java.io.FileInputStream;
  25. import java.io.FileOutputStream;
  26. import java.io.IOException;
  27. import java.io.InputStreamReader;
  28. import java.io.LineNumberReader;
  29. import java.io.OutputStreamWriter;
  30. import java.io.Writer;
  31. import java.net.Inet4Address;
  32. import java.net.InetAddress;
  33. import java.net.UnknownHostException;
  34. import java.util.List;
  35. import java.util.Locale;
  36. import java.util.NoSuchElementException;
  37. import java.util.ResourceBundle;
  38. import java.util.regex.Matcher;
  39. import java.util.regex.Pattern;
  40. import javax.swing.JButton;
  41. import javax.swing.JCheckBox;
  42. import javax.swing.JComponent;
  43. import javax.swing.JFrame;
  44. import javax.swing.JLabel;
  45. import javax.swing.JMenu;
  46. import javax.swing.JMenuBar;
  47. import javax.swing.JMenuItem;
  48. import javax.swing.JOptionPane;
  49. import javax.swing.JPanel;
  50. import javax.swing.JScrollPane;
  51. import javax.swing.JTabbedPane;
  52. import javax.swing.JTextArea;
  53. import javax.swing.JToolBar;
  54. import javax.swing.SwingUtilities;
  55. import javax.swing.ToolTipManager;
  56. import javax.swing.UIManager;
  57. import javolution.util.FastList;
  58. import com.l2jserver.configurator.ConfigUserInterface.ConfigFile.ConfigComment;
  59. import com.l2jserver.configurator.ConfigUserInterface.ConfigFile.ConfigProperty;
  60. import com.l2jserver.i18n.LanguageControl;
  61. import com.l2jserver.images.ImagesTable;
  62. /**
  63. *
  64. * @author KenM
  65. */
  66. public class ConfigUserInterface extends JFrame implements ActionListener
  67. {
  68. /**
  69. * Comment for <code>serialVersionUID</code>
  70. */
  71. private static final long serialVersionUID = 1L;
  72. private JTabbedPane _tabPane = new JTabbedPane();
  73. private List<ConfigFile> _configs = new FastList<ConfigFile>();
  74. private ResourceBundle _bundle;
  75. /**
  76. * @param args
  77. */
  78. public static void main(String[] args)
  79. {
  80. try
  81. {
  82. UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
  83. }
  84. catch (Exception e)
  85. {
  86. // couldn't care less
  87. }
  88. final ResourceBundle bundle = ResourceBundle.getBundle("configurator.Configurator", Locale.getDefault(), LanguageControl.INSTANCE);
  89. SwingUtilities.invokeLater
  90. (
  91. new Runnable()
  92. {
  93. @Override
  94. public void run()
  95. {
  96. ConfigUserInterface cui = new ConfigUserInterface(bundle);
  97. cui.setVisible(true);
  98. }
  99. }
  100. );
  101. }
  102. public ConfigUserInterface(ResourceBundle bundle)
  103. {
  104. setBundle(bundle);
  105. this.setTitle(bundle.getString("toolName"));
  106. this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  107. this.setSize(750, 500);
  108. this.setLayout(new GridBagLayout());
  109. GridBagConstraints cons = new GridBagConstraints();
  110. cons.fill = GridBagConstraints.HORIZONTAL;
  111. cons.gridx = 0;
  112. cons.gridy = 0;
  113. cons.weighty = 0;
  114. cons.weightx = 1;
  115. JMenuBar menubar = new JMenuBar();
  116. JMenu fileMenu = new JMenu(bundle.getString("fileMenu"));
  117. JMenu helpMenu = new JMenu(bundle.getString("helpMenu"));
  118. JMenuItem exitItem = new JMenuItem(bundle.getString("exitItem"));
  119. exitItem.setActionCommand("exit");
  120. exitItem.addActionListener(this);
  121. fileMenu.add(exitItem);
  122. JMenuItem aboutItem = new JMenuItem(bundle.getString("aboutItem"));
  123. aboutItem.setActionCommand("about");
  124. aboutItem.addActionListener(this);
  125. helpMenu.add(aboutItem);
  126. menubar.add(fileMenu);
  127. menubar.add(helpMenu);
  128. this.setJMenuBar(menubar);
  129. JToolBar toolBar = new JToolBar();
  130. toolBar.setFloatable(false);
  131. toolBar.setRollover(true);
  132. toolBar.add(this.createToolButton("disk.png", bundle.getString("save"), "save"));
  133. this.add(toolBar, cons);
  134. cons.gridy++;
  135. cons.fill = GridBagConstraints.BOTH;
  136. cons.weighty = 1;
  137. this.loadConfigs();
  138. this.buildInterface();
  139. this.add(_tabPane, cons);
  140. }
  141. private JButton createToolButton(String image, String text, String action)
  142. {
  143. JButton button = new JButton(text, ImagesTable.getImage(image));
  144. button.setActionCommand(action);
  145. button.addActionListener(this);
  146. return button;
  147. }
  148. /**
  149. *
  150. */
  151. @SuppressWarnings("serial")
  152. private void buildInterface()
  153. {
  154. ToolTipManager.sharedInstance().setDismissDelay(Integer.MAX_VALUE);
  155. ToolTipManager.sharedInstance().setInitialDelay(0);
  156. ToolTipManager.sharedInstance().setReshowDelay(0);
  157. GridBagConstraints cons = new GridBagConstraints();
  158. cons.fill = GridBagConstraints.NONE;
  159. cons.anchor = GridBagConstraints.FIRST_LINE_START;
  160. cons.insets = new Insets(2, 2, 2, 2);
  161. for (ConfigFile cf : getConfigs())
  162. {
  163. JPanel panel = new JPanel() {
  164. @Override
  165. public void scrollRectToVisible(Rectangle r ) {}
  166. };
  167. panel.setLayout(new GridBagLayout());
  168. cons.gridy = 0;
  169. cons.weighty = 0;
  170. for (ConfigComment cc : cf.getConfigProperties())
  171. {
  172. if (!(cc instanceof ConfigProperty))
  173. {
  174. continue;
  175. }
  176. ConfigProperty cp = (ConfigProperty) cc;
  177. cons.gridx = 0;
  178. JLabel keyLabel = new JLabel(cp.getDisplayName()+':', ImagesTable.getImage("help.png"), JLabel.LEFT);
  179. String comments = "<b>"+cp.getName()+":</b><br>"+cp.getComments();
  180. comments = comments.replace("\r\n", "<br>");
  181. comments = "<html>"+comments+"</html>";
  182. keyLabel.setToolTipText(comments);
  183. cons.weightx = 0;
  184. panel.add(keyLabel, cons);
  185. cons.gridx++;
  186. JComponent valueComponent = cp.getValueComponent();
  187. valueComponent.setToolTipText(comments);
  188. cons.weightx = 1;
  189. panel.add(valueComponent, cons);
  190. cons.gridx++;
  191. cons.gridy++;
  192. }
  193. cons.gridy++;
  194. cons.weighty = 1;
  195. panel.add(new JLabel(), cons); // filler
  196. _tabPane.addTab(cf.getName(), new JScrollPane(panel));
  197. }
  198. }
  199. /**
  200. *
  201. */
  202. private void loadConfigs()
  203. {
  204. File configsDir = new File("config");
  205. for (File file : configsDir.listFiles())
  206. {
  207. if (file.getName().endsWith(".properties") && file.isFile() && file.canWrite())
  208. {
  209. try
  210. {
  211. this.parsePropertiesFile(file);
  212. }
  213. catch (IOException e)
  214. {
  215. JOptionPane.showMessageDialog(ConfigUserInterface.this,getBundle().getString("errorReading")+file.getName(),getBundle().getString("error"),JOptionPane.ERROR_MESSAGE);
  216. System.exit(3);
  217. // e.printStackTrace();
  218. }
  219. }
  220. }
  221. }
  222. /**
  223. * @param file
  224. * @throws IOException
  225. */
  226. private void parsePropertiesFile(File file) throws IOException
  227. {
  228. LineNumberReader lnr = new LineNumberReader(new InputStreamReader(new FileInputStream(file)));
  229. String line;
  230. StringBuilder commentBuffer = new StringBuilder();
  231. ConfigFile cf = new ConfigFile(file);
  232. while ((line = lnr.readLine()) != null)
  233. {
  234. line = line.trim();
  235. if (line.startsWith("#"))
  236. {
  237. if (commentBuffer.length() > 0)
  238. {
  239. commentBuffer.append("\r\n");
  240. }
  241. commentBuffer.append(line.substring(1));
  242. }
  243. else if (line.length() == 0)
  244. {
  245. // blank line, reset comments
  246. if (commentBuffer.length() > 0)
  247. {
  248. cf.addConfigComment(commentBuffer.toString());
  249. }
  250. commentBuffer.setLength(0);
  251. }
  252. else if (line.indexOf('=') >= 0)
  253. {
  254. String[] kv = line.split("=");
  255. String key = kv[0].trim();
  256. StringBuilder value = new StringBuilder();
  257. if (kv.length > 1)
  258. {
  259. value.append(kv[1].trim());
  260. }
  261. if (line.indexOf('\\') >= 0)
  262. {
  263. while ((line = lnr.readLine()) != null && line.indexOf('\\') >= 0)
  264. {
  265. value.append("\r\n"+line);
  266. }
  267. value.append("\r\n"+line);
  268. }
  269. String comments = commentBuffer.toString();
  270. commentBuffer.setLength(0); //reset
  271. cf.addConfigProperty(key, parseValue(value.toString()), comments);
  272. }
  273. }
  274. getConfigs().add(cf);
  275. lnr.close();
  276. }
  277. /**
  278. * @param value
  279. */
  280. private Object parseValue(String value)
  281. {
  282. if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("true"))
  283. {
  284. return Boolean.parseBoolean(value);
  285. }
  286. /*try
  287. {
  288. double parseDouble = Double.parseDouble(value);
  289. return parseDouble;
  290. }
  291. catch (NumberFormatException e)
  292. {
  293. // not a double, ignore
  294. }*/
  295. // localhost -> 127.0.0.1
  296. if (value.equals("localhost"))
  297. {
  298. value = "127.0.0.1";
  299. }
  300. String[] parts = value.split("\\.");
  301. if (parts.length == 4)
  302. {
  303. boolean ok = true;
  304. for (int i = 0; i < 4 && ok; i++)
  305. {
  306. try
  307. {
  308. int parseInt = Integer.parseInt(parts[i]);
  309. if (parseInt < 0 || parseInt > 255)
  310. {
  311. ok = false;
  312. }
  313. }
  314. catch (NumberFormatException e)
  315. {
  316. ok = false;
  317. }
  318. }
  319. if (ok)
  320. {
  321. try
  322. {
  323. InetAddress address = InetAddress.getByName(value);
  324. return address;
  325. }
  326. catch (UnknownHostException e)
  327. {
  328. // ignore
  329. }
  330. }
  331. }
  332. return value;
  333. }
  334. static class ConfigFile
  335. {
  336. private File _file;
  337. private String _name;
  338. private final List<ConfigComment> _configs = new FastList<ConfigComment>();
  339. public ConfigFile(File file)
  340. {
  341. _file = file;
  342. int lastIndex = file.getName().lastIndexOf('.');
  343. setName(file.getName().substring(0, lastIndex));
  344. }
  345. public void addConfigProperty(String name, Object value, ValueType type, String comments)
  346. {
  347. _configs.add(new ConfigProperty(name, value, type, comments));
  348. }
  349. public void addConfigComment(String comment)
  350. {
  351. _configs.add(new ConfigComment(comment));
  352. }
  353. public void addConfigProperty(String name, Object value, String comments)
  354. {
  355. this.addConfigProperty(name, value, ValueType.firstTypeMatch(value), comments);
  356. }
  357. public List<ConfigComment> getConfigProperties()
  358. {
  359. return _configs;
  360. }
  361. /**
  362. * @param name The name to set.
  363. */
  364. public void setName(String name)
  365. {
  366. _name = name;
  367. }
  368. /**
  369. * @return Returns the name.
  370. */
  371. public String getName()
  372. {
  373. return _name;
  374. }
  375. public void save() throws IOException
  376. {
  377. BufferedWriter bufWriter = null;
  378. try
  379. {
  380. bufWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(_file)));
  381. for (ConfigComment cc : _configs)
  382. {
  383. cc.save(bufWriter);
  384. }
  385. }
  386. finally
  387. {
  388. if (bufWriter != null)
  389. {
  390. bufWriter.close();
  391. }
  392. }
  393. }
  394. class ConfigComment
  395. {
  396. private String _comments;
  397. /**
  398. * @param comments
  399. */
  400. public ConfigComment(String comments)
  401. {
  402. _comments = comments;
  403. }
  404. /**
  405. * @return Returns the comments.
  406. */
  407. public String getComments()
  408. {
  409. return _comments;
  410. }
  411. /**
  412. * @param comments The comments to set.
  413. */
  414. public void setComments(String comments)
  415. {
  416. _comments = comments;
  417. }
  418. public void save(Writer writer) throws IOException
  419. {
  420. StringBuilder sb = new StringBuilder();
  421. sb.append('#');
  422. sb.append(this.getComments().replace("\r\n", "\r\n#"));
  423. sb.append("\r\n\r\n");
  424. writer.write(sb.toString());
  425. }
  426. }
  427. class ConfigProperty extends ConfigComment
  428. {
  429. private String _propname;
  430. private Object _value;
  431. private ValueType _type;
  432. private JComponent _component;
  433. /**
  434. * @param name
  435. * @param value
  436. * @param type
  437. * @param comments
  438. */
  439. public ConfigProperty(String name, Object value, ValueType type, String comments)
  440. {
  441. super(comments);
  442. if (!type.getType().isAssignableFrom(value.getClass()))
  443. {
  444. throw new IllegalArgumentException("Value Instance Type doesn't match the type argument.");
  445. }
  446. _propname = name;
  447. _type = type;
  448. _value = value;
  449. }
  450. /**
  451. * @return Returns the name.
  452. */
  453. public String getName()
  454. {
  455. return _propname;
  456. }
  457. /**
  458. * @return Returns the name.
  459. */
  460. public String getDisplayName()
  461. {
  462. return unCamelize(_propname);
  463. }
  464. /**
  465. * @param name The name to set.
  466. */
  467. public void setName(String name)
  468. {
  469. _propname = name;
  470. }
  471. /**
  472. * @return Returns the value.
  473. */
  474. public Object getValue()
  475. {
  476. return _value;
  477. }
  478. /**
  479. * @param value The value to set.
  480. */
  481. public void setValue(String value)
  482. {
  483. _value = value;
  484. }
  485. /**
  486. * @return Returns the type.
  487. */
  488. public ValueType getType()
  489. {
  490. return _type;
  491. }
  492. /**
  493. * @param type The type to set.
  494. */
  495. public void setType(ValueType type)
  496. {
  497. _type = type;
  498. }
  499. public JComponent getValueComponent()
  500. {
  501. if (_component == null)
  502. {
  503. _component = createValueComponent();
  504. }
  505. return _component;
  506. }
  507. public JComponent createValueComponent()
  508. {
  509. switch (this.getType())
  510. {
  511. case BOOLEAN:
  512. boolean bool = (Boolean) this.getValue();
  513. JCheckBox checkBox = new JCheckBox();
  514. checkBox.setSelected(bool);
  515. return checkBox;
  516. case IPv4:
  517. return new JIPTextField((Inet4Address) this.getValue());
  518. case DOUBLE:
  519. case INTEGER:
  520. case STRING:
  521. default:
  522. String val = this.getValue().toString();
  523. JTextArea textArea = new JTextArea(val);
  524. textArea.setFont(UIManager.getFont("TextField.font"));
  525. int rows = 1;
  526. for (int i = 0; i < val.length(); i++)
  527. if (val.charAt(i) == '\\')
  528. rows++;
  529. textArea.setRows(rows);
  530. textArea.setColumns(Math.max(val.length() / rows, 20));
  531. return textArea;
  532. }
  533. }
  534. @Override
  535. public void save(Writer writer) throws IOException
  536. {
  537. String value;
  538. if (this.getValueComponent() instanceof JCheckBox)
  539. {
  540. value = Boolean.toString(((JCheckBox) this.getValueComponent()).isSelected());
  541. value = value.substring(0, 1).toUpperCase() + value.substring(1);
  542. }
  543. else if (this.getValueComponent() instanceof JIPTextField)
  544. {
  545. value = ((JIPTextField) this.getValueComponent()).getText();
  546. }
  547. else if (this.getValueComponent() instanceof JTextArea)
  548. {
  549. value = ((JTextArea) this.getValueComponent()).getText();
  550. }
  551. else
  552. {
  553. throw new IllegalStateException("Unhandled component value");
  554. }
  555. StringBuilder sb = new StringBuilder();
  556. sb.append('#');
  557. sb.append(this.getComments().replace("\r\n", "\r\n#"));
  558. sb.append("\r\n");
  559. sb.append(this.getName());
  560. sb.append(" = ");
  561. sb.append(value);
  562. sb.append("\r\n");
  563. sb.append("\r\n");
  564. writer.write(sb.toString());
  565. }
  566. }
  567. }
  568. public static enum ValueType
  569. {
  570. BOOLEAN (Boolean.class),
  571. DOUBLE (Double.class),
  572. INTEGER (Integer.class),
  573. IPv4 (Inet4Address.class),
  574. STRING (String.class);
  575. private final Class<?> _type;
  576. private ValueType(Class<?> type)
  577. {
  578. _type = type;
  579. }
  580. /**
  581. * @return Returns the type.
  582. */
  583. public Class<?> getType()
  584. {
  585. return _type;
  586. }
  587. public static ValueType firstTypeMatch(Object value)
  588. {
  589. for (ValueType vt : ValueType.values())
  590. {
  591. if (vt.getType() == value.getClass())
  592. {
  593. return vt;
  594. }
  595. }
  596. throw new NoSuchElementException("No match for: "+value.getClass().getName());
  597. }
  598. }
  599. /**
  600. * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
  601. */
  602. @Override
  603. public void actionPerformed(ActionEvent e)
  604. {
  605. String cmd = e.getActionCommand();
  606. StringBuilder errors = new StringBuilder();
  607. if (cmd.equals("save"))
  608. {
  609. for (ConfigFile cf : ConfigUserInterface.this.getConfigs())
  610. {
  611. try
  612. {
  613. cf.save();
  614. }
  615. catch (Exception e1)
  616. {
  617. e1.printStackTrace();
  618. errors.append(getBundle().getString("errorSaving")+cf.getName()+".properties. "+getBundle().getString("reason")+e1.getLocalizedMessage()+"\r\n");
  619. }
  620. }
  621. if (errors.length() == 0)
  622. {
  623. JOptionPane.showMessageDialog(ConfigUserInterface.this, getBundle().getString("success"), "OK", JOptionPane.INFORMATION_MESSAGE);
  624. }
  625. else
  626. {
  627. JOptionPane.showMessageDialog(ConfigUserInterface.this,errors,getBundle().getString("error"),JOptionPane.ERROR_MESSAGE);
  628. System.exit(2);
  629. }
  630. }
  631. else if (cmd.equals("exit"))
  632. {
  633. System.exit(0);
  634. }
  635. else if (cmd.equals("about"))
  636. {
  637. JOptionPane.showMessageDialog(ConfigUserInterface.this, getBundle().getString("credits") + "\nhttp://www.l2jserver.com\n\n"+getBundle().getString("icons")+"\n\n"+getBundle().getString("language")+'\n'+getBundle().getString("translation"), getBundle().getString("aboutItem"), JOptionPane.INFORMATION_MESSAGE, ImagesTable.getImage("l2jserverlogo.png"));
  638. }
  639. }
  640. /**
  641. * @param configs The configuration to set.
  642. */
  643. public void setConfigs(List<ConfigFile> configs)
  644. {
  645. _configs = configs;
  646. }
  647. /**
  648. * @return Returns the configuration.
  649. */
  650. public List<ConfigFile> getConfigs()
  651. {
  652. return _configs;
  653. }
  654. /**
  655. * @return Returns the configuration setting name in a
  656. * human readable form.
  657. */
  658. public static String unCamelize(final String keyName) {
  659. Pattern p = Pattern.compile("\\p{Lu}");
  660. Matcher m = p.matcher(keyName);
  661. StringBuffer sb = new StringBuffer();
  662. int last = 0;
  663. while (m.find())
  664. {
  665. if (m.start() != last + 1)
  666. {
  667. m.appendReplacement(sb," " + m.group());
  668. }
  669. last = m.start();
  670. }
  671. m.appendTail(sb);
  672. return sb.toString().trim();
  673. }
  674. /**
  675. * @param bundle The bundle to set.
  676. */
  677. public void setBundle(ResourceBundle bundle)
  678. {
  679. _bundle = bundle;
  680. }
  681. /**
  682. * @return Returns the bundle.
  683. */
  684. public ResourceBundle getBundle()
  685. {
  686. return _bundle;
  687. }
  688. }