ConfigUserInterface.java 20 KB

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