46 import java.util.LinkedList;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.ResourceBundle;
50 import java.util.Set;
51 import java.util.regex.Pattern;
52
53 import javafx.beans.DefaultProperty;
54 import javafx.beans.InvalidationListener;
55 import javafx.beans.property.Property;
56 import javafx.beans.value.ChangeListener;
57 import javafx.beans.value.ObservableValue;
58 import javafx.collections.*;
59 import javafx.event.Event;
60 import javafx.event.EventHandler;
61 import javafx.util.Builder;
62 import javafx.util.BuilderFactory;
63 import javafx.util.Callback;
64
65 import javax.script.Bindings;
66 import javax.script.ScriptContext;
67 import javax.script.ScriptEngine;
68 import javax.script.ScriptEngineManager;
69 import javax.script.ScriptException;
70 import javax.script.SimpleBindings;
71 import javax.xml.stream.XMLInputFactory;
72 import javax.xml.stream.XMLStreamConstants;
73 import javax.xml.stream.XMLStreamException;
74 import javax.xml.stream.XMLStreamReader;
75 import javax.xml.stream.util.StreamReaderDelegate;
76
77 import com.sun.javafx.beans.IDProperty;
78 import com.sun.javafx.fxml.BeanAdapter;
79 import com.sun.javafx.fxml.ParseTraceElement;
80 import com.sun.javafx.fxml.PropertyNotFoundException;
81 import com.sun.javafx.fxml.expression.Expression;
82 import com.sun.javafx.fxml.expression.ExpressionValue;
83 import com.sun.javafx.fxml.expression.KeyPath;
84 import static com.sun.javafx.FXPermissions.MODIFY_FXML_CLASS_LOADER_PERMISSION;
85 import com.sun.javafx.fxml.FXMLLoaderHelper;
1541 }
1542
1543 if (engine == null) {
1544 throw constructLoadException("Unable to locate scripting engine for"
1545 + " extension " + extension + ".");
1546 }
1547
1548 try {
1549 URL location;
1550 if (source.charAt(0) == '/') {
1551 // FIXME: JIGSAW -- use Class.getResourceAsStream if resource is in a module
1552 location = cl.getResource(source.substring(1));
1553 } else {
1554 if (FXMLLoader.this.location == null) {
1555 throw constructLoadException("Base location is undefined.");
1556 }
1557
1558 location = new URL(FXMLLoader.this.location, source);
1559 }
1560 Bindings engineBindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
1561 engineBindings.put(engine.FILENAME, location.getPath());
1562
1563 InputStreamReader scriptReader = null;
1564 try {
1565 scriptReader = new InputStreamReader(location.openStream(), charset);
1566 engine.eval(scriptReader);
1567 } catch(ScriptException exception) {
1568 exception.printStackTrace();
1569 } finally {
1570 if (scriptReader != null) {
1571 scriptReader.close();
1572 }
1573 }
1574 } catch (IOException exception) {
1575 throw constructLoadException(exception);
1576 }
1577 }
1578 }
1579
1580 @Override
1581 public void processEndElement() throws IOException {
1582 super.processEndElement();
1583
1584 if (value != null && !staticLoad) {
1585 // Evaluate the script
1586 try {
1587 Bindings engineBindings = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
1588 engineBindings.put(scriptEngine.FILENAME, location.getPath() + "-script_starting_at_line_"
1589 + (getLineNumber() - (int) ((String) value).codePoints().filter(c -> c == '\n').count()));
1590 scriptEngine.eval((String)value);
1591 } catch (ScriptException exception) {
1592 System.err.println(exception.getMessage());
1593 }
1594 }
1595 }
1596
1597 @Override
1598 public void processCharacters() throws LoadException {
1599 if (source != null) {
1600 throw constructLoadException("Script source already specified.");
1601 }
1602
1603 if (scriptEngine == null && !staticLoad) {
1604 throw constructLoadException("Page language not specified.");
1605 }
1606
1607 updateValue(xmlStreamReader.getText());
1608 }
1609
1610 @Override
1611 public void processAttribute(String prefix, String localName, String value)
1612 throws IOException {
1664
1665 // Event handler that delegates to a method defined by the controller object
1666 private static class ControllerMethodEventHandler<T extends Event> implements EventHandler<T> {
1667 private final MethodHandler handler;
1668
1669 public ControllerMethodEventHandler(MethodHandler handler) {
1670 this.handler = handler;
1671 }
1672
1673 @Override
1674 public void handle(T event) {
1675 handler.invoke(event);
1676 }
1677 }
1678
1679 // Event handler implemented in script code
1680 private static class ScriptEventHandler implements EventHandler<Event> {
1681 public final String script;
1682 public final ScriptEngine scriptEngine;
1683 public final String filename;
1684
1685 public ScriptEventHandler(String script, ScriptEngine scriptEngine, String filename) {
1686 this.script = script;
1687 this.scriptEngine = scriptEngine;
1688 this.filename = filename;
1689 }
1690
1691 @Override
1692 public void handle(Event event) {
1693 // Don't pollute the page namespace with values defined in the script
1694 Bindings engineBindings = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
1695 Bindings localBindings = scriptEngine.createBindings();
1696 localBindings.putAll(engineBindings);
1697 localBindings.put(EVENT_KEY, event);
1698 localBindings.put(scriptEngine.ARGV, new Object[]{event});
1699 localBindings.put(scriptEngine.FILENAME, filename);
1700 // Execute the script
1701 try {
1702 scriptEngine.eval(script, localBindings);
1703 } catch (ScriptException exception){
1704 throw new RuntimeException(exception);
1705 }
1706 }
1707 }
1708
1709 // Observable list change listener
1710 private static class ObservableListChangeAdapter implements ListChangeListener {
1711 private final MethodHandler handler;
1712
1713 public ObservableListChangeAdapter(MethodHandler handler) {
1714 this.handler = handler;
1715 }
1716
1717 @Override
1718 @SuppressWarnings("unchecked")
1719 public void onChanged(Change change) {
1720 if (handler != null) {
1721 handler.invoke(change);
1722 }
1723 }
1724 }
1802
1803 private Object root = null;
1804 private Object controller = null;
1805
1806 private BuilderFactory builderFactory;
1807 private Callback<Class<?>, Object> controllerFactory;
1808 private Charset charset;
1809
1810 private final LinkedList<FXMLLoader> loaders;
1811
1812 private ClassLoader classLoader = null;
1813 private boolean staticLoad = false;
1814 private LoadListener loadListener = null;
1815
1816 private FXMLLoader parentLoader;
1817
1818 private XMLStreamReader xmlStreamReader = null;
1819 private Element current = null;
1820
1821 private ScriptEngine scriptEngine = null;
1822
1823 private List<String> packages = new LinkedList<String>();
1824 private Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
1825
1826 private ScriptEngineManager scriptEngineManager = null;
1827
1828 private static ClassLoader defaultClassLoader = null;
1829
1830 private static final Pattern extraneousWhitespacePattern = Pattern.compile("\\s+");
1831
1832 private static BuilderFactory DEFAULT_BUILDER_FACTORY = new JavaFXBuilderFactory();
1833
1834 /**
1835 * The character set used when character set is not explicitly specified.
1836 */
1837 public static final String DEFAULT_CHARSET_NAME = "UTF-8";
1838
1839 /**
1840 * The tag name of language processing instruction.
1841 */
1842 public static final String LANGUAGE_PROCESSING_INSTRUCTION = "language";
1843 /**
1844 * The tag name of import processing instruction.
1845 */
1846 public static final String IMPORT_PROCESSING_INSTRUCTION = "import";
1847
1848 /**
1849 * Prefix of 'fx' namespace.
1850 */
1851 public static final String FX_NAMESPACE_PREFIX = "fx";
1852 /**
1853 * The name of fx:controller attribute of a root.
1854 */
1855 public static final String FX_CONTROLLER_ATTRIBUTE = "controller";
1856 /**
1857 * The name of fx:id attribute.
1858 */
1859 public static final String FX_ID_ATTRIBUTE = "id";
1860 /**
1861 * The name of fx:value attribute.
1862 */
1863 public static final String FX_VALUE_ATTRIBUTE = "value";
1864 /**
1865 * The tag name of 'fx:constant'.
1866 * @since JavaFX 2.2
1867 */
2661 */
2662 ParseTraceElement[] getParseTrace() {
2663 ParseTraceElement[] parseTrace = new ParseTraceElement[loaders.size()];
2664
2665 int i = 0;
2666 for (FXMLLoader loader : loaders) {
2667 parseTrace[i++] = new ParseTraceElement(loader.location, (loader.current != null) ?
2668 loader.getLineNumber() : -1);
2669 }
2670
2671 return parseTrace;
2672 }
2673
2674 private void processProcessingInstruction() throws LoadException {
2675 String piTarget = xmlStreamReader.getPITarget().trim();
2676
2677 if (piTarget.equals(LANGUAGE_PROCESSING_INSTRUCTION)) {
2678 processLanguage();
2679 } else if (piTarget.equals(IMPORT_PROCESSING_INSTRUCTION)) {
2680 processImport();
2681 }
2682 }
2683
2684 private void processLanguage() throws LoadException {
2685 if (scriptEngine != null) {
2686 throw constructLoadException("Page language already set.");
2687 }
2688
2689 String language = xmlStreamReader.getPIData();
2690
2691 if (loadListener != null) {
2692 loadListener.readLanguageProcessingInstruction(language);
2693 }
2694
2695 if (!staticLoad) {
2696 ScriptEngineManager scriptEngineManager = getScriptEngineManager();
2697 scriptEngine = scriptEngineManager.getEngineByName(language);
2698 }
2699 }
2700
|
46 import java.util.LinkedList;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.ResourceBundle;
50 import java.util.Set;
51 import java.util.regex.Pattern;
52
53 import javafx.beans.DefaultProperty;
54 import javafx.beans.InvalidationListener;
55 import javafx.beans.property.Property;
56 import javafx.beans.value.ChangeListener;
57 import javafx.beans.value.ObservableValue;
58 import javafx.collections.*;
59 import javafx.event.Event;
60 import javafx.event.EventHandler;
61 import javafx.util.Builder;
62 import javafx.util.BuilderFactory;
63 import javafx.util.Callback;
64
65 import javax.script.Bindings;
66 import javax.script.Compilable;
67 import javax.script.CompiledScript;
68 import javax.script.ScriptContext;
69 import javax.script.ScriptEngine;
70 import javax.script.ScriptEngineManager;
71 import javax.script.ScriptException;
72 import javax.script.SimpleBindings;
73 import javax.xml.stream.XMLInputFactory;
74 import javax.xml.stream.XMLStreamConstants;
75 import javax.xml.stream.XMLStreamException;
76 import javax.xml.stream.XMLStreamReader;
77 import javax.xml.stream.util.StreamReaderDelegate;
78
79 import com.sun.javafx.beans.IDProperty;
80 import com.sun.javafx.fxml.BeanAdapter;
81 import com.sun.javafx.fxml.ParseTraceElement;
82 import com.sun.javafx.fxml.PropertyNotFoundException;
83 import com.sun.javafx.fxml.expression.Expression;
84 import com.sun.javafx.fxml.expression.ExpressionValue;
85 import com.sun.javafx.fxml.expression.KeyPath;
86 import static com.sun.javafx.FXPermissions.MODIFY_FXML_CLASS_LOADER_PERMISSION;
87 import com.sun.javafx.fxml.FXMLLoaderHelper;
1543 }
1544
1545 if (engine == null) {
1546 throw constructLoadException("Unable to locate scripting engine for"
1547 + " extension " + extension + ".");
1548 }
1549
1550 try {
1551 URL location;
1552 if (source.charAt(0) == '/') {
1553 // FIXME: JIGSAW -- use Class.getResourceAsStream if resource is in a module
1554 location = cl.getResource(source.substring(1));
1555 } else {
1556 if (FXMLLoader.this.location == null) {
1557 throw constructLoadException("Base location is undefined.");
1558 }
1559
1560 location = new URL(FXMLLoader.this.location, source);
1561 }
1562 Bindings engineBindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
1563 String filename = location.getPath();
1564 engineBindings.put(engine.FILENAME, filename);
1565
1566 InputStreamReader scriptReader = null;
1567 String script=null;
1568 try {
1569 scriptReader = new InputStreamReader(location.openStream(), charset);
1570 StringBuilder sb = new StringBuilder();
1571 final int bufSize = 4096;
1572 char[] charBuffer = new char[bufSize];
1573 int n;
1574 do {
1575 n = scriptReader.read(charBuffer,0,bufSize);
1576 if (n > 0) {
1577 sb.append(new String(charBuffer,0,n));
1578 }
1579 } while (n > -1);
1580 script = sb.toString();
1581 } catch (IOException exception) {
1582 throw constructLoadException(exception);
1583 } finally {
1584 if (scriptReader != null) {
1585 scriptReader.close();
1586 }
1587 }
1588 try {
1589 if (engine instanceof Compilable && compileScript) {
1590 CompiledScript compiledScript = null;
1591 try {
1592 compiledScript=((Compilable) engine).compile(script);
1593 } catch (ScriptException compileExc) {
1594 Logging.getJavaFXLogger().warning(filename + ": compiling caused \"" + compileExc +
1595 "\", falling back to evaluating script in uncompiled mode");
1596 }
1597 if (compiledScript != null) {
1598 compiledScript.eval();
1599 } else { // fallback to uncompiled mode
1600 engine.eval(script);
1601 }
1602 } else {
1603 engine.eval(script);
1604 }
1605 } catch (ScriptException exception) {
1606 System.err.println(filename + ": caused ScriptException");
1607 exception.printStackTrace();
1608 }
1609 }
1610 catch (IOException exception) {
1611 throw constructLoadException(exception);
1612 }
1613 }
1614 }
1615
1616 @Override
1617 public void processEndElement() throws IOException {
1618 super.processEndElement();
1619
1620 if (value != null && !staticLoad) {
1621 // Evaluate the script
1622 String filename = null;
1623 try {
1624 Bindings engineBindings = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
1625 String script = (String) value;
1626 filename = location.getPath() + "-script_starting_at_line_"
1627 + (getLineNumber() - (int) script.codePoints().filter(c -> c == '\n').count());
1628 engineBindings.put(scriptEngine.FILENAME, filename);
1629 if (scriptEngine instanceof Compilable && compileScript) {
1630 CompiledScript compiledScript = null;
1631 try {
1632 compiledScript=((Compilable) scriptEngine).compile(script);
1633 } catch (ScriptException compileExc) {
1634 Logging.getJavaFXLogger().warning(filename + ": compiling caused \"" + compileExc +
1635 "\", falling back to evaluating script in uncompiled mode");
1636 }
1637 if (compiledScript != null) {
1638 compiledScript.eval();
1639 } else { // fallback to uncompiled mode
1640 scriptEngine.eval(script);
1641 }
1642 } else {
1643 scriptEngine.eval(script);
1644 }
1645 } catch (ScriptException exception) {
1646 System.err.println(filename + ": caused ScriptException\n" + exception.getMessage());
1647 }
1648 }
1649 }
1650
1651 @Override
1652 public void processCharacters() throws LoadException {
1653 if (source != null) {
1654 throw constructLoadException("Script source already specified.");
1655 }
1656
1657 if (scriptEngine == null && !staticLoad) {
1658 throw constructLoadException("Page language not specified.");
1659 }
1660
1661 updateValue(xmlStreamReader.getText());
1662 }
1663
1664 @Override
1665 public void processAttribute(String prefix, String localName, String value)
1666 throws IOException {
1718
1719 // Event handler that delegates to a method defined by the controller object
1720 private static class ControllerMethodEventHandler<T extends Event> implements EventHandler<T> {
1721 private final MethodHandler handler;
1722
1723 public ControllerMethodEventHandler(MethodHandler handler) {
1724 this.handler = handler;
1725 }
1726
1727 @Override
1728 public void handle(T event) {
1729 handler.invoke(event);
1730 }
1731 }
1732
1733 // Event handler implemented in script code
1734 private static class ScriptEventHandler implements EventHandler<Event> {
1735 public final String script;
1736 public final ScriptEngine scriptEngine;
1737 public final String filename;
1738 public CompiledScript compiledScript;
1739 public boolean isCompiled=false;
1740
1741 public ScriptEventHandler(String script, ScriptEngine scriptEngine, String filename) {
1742 this.script = script;
1743 this.scriptEngine = scriptEngine;
1744 this.filename = filename;
1745 if (scriptEngine instanceof Compilable && compileScript) {
1746 try {
1747 // supply the filename to the scriptEngine engine scope Bindings in case it is needed for compilation
1748 scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).put(scriptEngine.FILENAME, filename);
1749 this.compiledScript = ((Compilable) scriptEngine).compile(script);
1750 this.isCompiled = true;
1751 } catch (ScriptException compileExc) {
1752 Logging.getJavaFXLogger().warning(filename + ": compiling caused \"" + compileExc +
1753 "\", falling back to evaluating script in uncompiled mode");
1754 }
1755 }
1756 }
1757
1758 @Override
1759 public void handle(Event event) {
1760 // Don't pollute the page namespace with values defined in the script
1761 Bindings engineBindings = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
1762 Bindings localBindings = scriptEngine.createBindings();
1763 localBindings.putAll(engineBindings);
1764 localBindings.put(EVENT_KEY, event);
1765 localBindings.put(scriptEngine.ARGV, new Object[]{event});
1766 localBindings.put(scriptEngine.FILENAME, filename);
1767 // Execute the script
1768 try {
1769 if (isCompiled) {
1770 compiledScript.eval(localBindings);
1771 } else {
1772 scriptEngine.eval(script, localBindings);
1773 }
1774 } catch (ScriptException exception) {
1775 throw new RuntimeException(filename + ": caused ScriptException", exception);
1776 }
1777 }
1778 }
1779
1780 // Observable list change listener
1781 private static class ObservableListChangeAdapter implements ListChangeListener {
1782 private final MethodHandler handler;
1783
1784 public ObservableListChangeAdapter(MethodHandler handler) {
1785 this.handler = handler;
1786 }
1787
1788 @Override
1789 @SuppressWarnings("unchecked")
1790 public void onChanged(Change change) {
1791 if (handler != null) {
1792 handler.invoke(change);
1793 }
1794 }
1795 }
1873
1874 private Object root = null;
1875 private Object controller = null;
1876
1877 private BuilderFactory builderFactory;
1878 private Callback<Class<?>, Object> controllerFactory;
1879 private Charset charset;
1880
1881 private final LinkedList<FXMLLoader> loaders;
1882
1883 private ClassLoader classLoader = null;
1884 private boolean staticLoad = false;
1885 private LoadListener loadListener = null;
1886
1887 private FXMLLoader parentLoader;
1888
1889 private XMLStreamReader xmlStreamReader = null;
1890 private Element current = null;
1891
1892 private ScriptEngine scriptEngine = null;
1893 private static boolean compileScript = true;
1894
1895 private List<String> packages = new LinkedList<String>();
1896 private Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
1897
1898 private ScriptEngineManager scriptEngineManager = null;
1899
1900 private static ClassLoader defaultClassLoader = null;
1901
1902 private static final Pattern extraneousWhitespacePattern = Pattern.compile("\\s+");
1903
1904 private static BuilderFactory DEFAULT_BUILDER_FACTORY = new JavaFXBuilderFactory();
1905
1906 /**
1907 * The character set used when character set is not explicitly specified.
1908 */
1909 public static final String DEFAULT_CHARSET_NAME = "UTF-8";
1910
1911 /**
1912 * The tag name of language processing instruction.
1913 */
1914 public static final String LANGUAGE_PROCESSING_INSTRUCTION = "language";
1915 /**
1916 * The tag name of import processing instruction.
1917 */
1918 public static final String IMPORT_PROCESSING_INSTRUCTION = "import";
1919
1920 /**
1921 * The tag name of the compile processing instruction.
1922 * @since 15
1923 */
1924 public static final String COMPILE_PROCESSING_INSTRUCTION = "compile";
1925
1926 /**
1927 * Prefix of 'fx' namespace.
1928 */
1929 public static final String FX_NAMESPACE_PREFIX = "fx";
1930 /**
1931 * The name of fx:controller attribute of a root.
1932 */
1933 public static final String FX_CONTROLLER_ATTRIBUTE = "controller";
1934 /**
1935 * The name of fx:id attribute.
1936 */
1937 public static final String FX_ID_ATTRIBUTE = "id";
1938 /**
1939 * The name of fx:value attribute.
1940 */
1941 public static final String FX_VALUE_ATTRIBUTE = "value";
1942 /**
1943 * The tag name of 'fx:constant'.
1944 * @since JavaFX 2.2
1945 */
2739 */
2740 ParseTraceElement[] getParseTrace() {
2741 ParseTraceElement[] parseTrace = new ParseTraceElement[loaders.size()];
2742
2743 int i = 0;
2744 for (FXMLLoader loader : loaders) {
2745 parseTrace[i++] = new ParseTraceElement(loader.location, (loader.current != null) ?
2746 loader.getLineNumber() : -1);
2747 }
2748
2749 return parseTrace;
2750 }
2751
2752 private void processProcessingInstruction() throws LoadException {
2753 String piTarget = xmlStreamReader.getPITarget().trim();
2754
2755 if (piTarget.equals(LANGUAGE_PROCESSING_INSTRUCTION)) {
2756 processLanguage();
2757 } else if (piTarget.equals(IMPORT_PROCESSING_INSTRUCTION)) {
2758 processImport();
2759 } else if (piTarget.equals(COMPILE_PROCESSING_INSTRUCTION)) {
2760 String strCompile=xmlStreamReader.getPIData().trim();
2761 // if PIData() is empty string then default to true, otherwise use Boolean.parseBoolean(string) to determine the boolean value
2762 compileScript = (strCompile.length()==0 ? true : Boolean.parseBoolean(strCompile));
2763 }
2764 }
2765
2766 private void processLanguage() throws LoadException {
2767 if (scriptEngine != null) {
2768 throw constructLoadException("Page language already set.");
2769 }
2770
2771 String language = xmlStreamReader.getPIData();
2772
2773 if (loadListener != null) {
2774 loadListener.readLanguageProcessingInstruction(language);
2775 }
2776
2777 if (!staticLoad) {
2778 ScriptEngineManager scriptEngineManager = getScriptEngineManager();
2779 scriptEngine = scriptEngineManager.getEngineByName(language);
2780 }
2781 }
2782
|