All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javafx.css.Stylesheet Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.css;

import javafx.css.StyleConverter.StringStore;

import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;

import com.sun.javafx.collections.TrackableObservableList;
import com.sun.javafx.css.FontFaceImpl;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * A stylesheet which can apply properties to a tree of objects.  A stylesheet
 * is a collection of zero or more {@link Rule Rules}, each of which is applied
 * to each object in the tree.  Typically the selector will examine the object to
 * determine whether or not it is applicable, and if so it will apply certain
 * property values to the object.
 *
 * @since 9
 */
public class Stylesheet {

    /**
     * Version number of binary CSS format. The value is incremented whenever the format of the
     * binary stream changes. This number does not correlate with JavaFX versions.
     * Version 5: persist @font-face
     * Version 6: converter classes moved to public package
     */
    final static int BINARY_CSS_VERSION = 6;

    private final String url;
    /**
     *  The URL from which this {@code Stylesheet} was loaded.
     *
     * @return A {@code String} representation of the URL from which the stylesheet was loaded, or {@code null} if
     *         the stylesheet was created from an inline style.
     */
    public String getUrl() {
        return url;
    }

    /**
     * Specifies the origin of this {@code Stylesheet}. We need to know this so
     * that we can make user important styles have higher priority than
     * author styles.
     */
    private StyleOrigin origin = StyleOrigin.AUTHOR;

    /**
     * Returns the origin of this {@code Stylesheet}.
     *
     * @return the origin of this {@code Stylesheet}
     */
    public StyleOrigin getOrigin() {
        return origin;
    }

    /**
     * Sets the origin of this {@code Stylesheet}.

     * @param origin the origin of this {@code Stylesheet}
     */
    public void setOrigin(StyleOrigin origin) {
        this.origin = origin;
    }

    /** All the rules contained in the stylesheet in the order they are in the file */
    private final ObservableList rules = new TrackableObservableList<>() {

        @Override protected void onChanged(Change c) {
            c.reset();
            while (c.next()) {
                if (c.wasAdded()) {
                    for(Rule rule : c.getAddedSubList()) {
                        rule.setStylesheet(Stylesheet.this);
                    }
                } else if (c.wasRemoved()) {
                    for (Rule rule : c.getRemoved()) {
                        if (rule.getStylesheet() == Stylesheet.this) rule.setStylesheet(null);
                    }
                }
            }
        }
    };

    /** List of all font faces */
    private final List fontFaces = new ArrayList<>();

    /**
     * Constructs a stylesheet with the base URI defaulting to the root
     * path of the application.
     */
    Stylesheet() {

//        ClassLoader cl = Thread.currentThread().getContextClassLoader();
//        this.url = (cl != null) ? cl.getResource("") : null;
        //
        // RT-17344
        // The above code is unreliable. The getResource call is intended
        // to return the root path of the Application instance, but it sometimes
        // returns null. Here, we'll set url to null and then when a url is
        // resolved, the url path can be used in the getResource call. For
        // example, if the css is -fx-image: url("images/duke.png"), we can
        // do cl.getResouce("images/duke.png") in URLConverter
        //

        this(null);
    }

    /**
     * Constructs a Stylesheet using the given URL as the base URI. The
     * parameter may not be null.
     *
     * @param url the base URI for this stylesheet
     */
    Stylesheet(String url) {
        this.url = url;
    }

    /**
     * Returns the rules that are defined in this {@code Stylesheet}.
     *
     * @return a list of rules used by this {@code Stylesheet}
     */
    public List getRules() {
        return rules;
    }

    /**
     * Returns the font faces used by this {@code Stylesheet}.
     *
     * @return a list of font faces used by this {@code Stylesheet}
     */
    public List getFontFaces() {
        return fontFaces;
    }

    /**
     * Indicates whether this {@code Stylesheet} is "equal to" some other object. Equality of two {@code Stylesheet}s is
     * based on the equality of their URL as defined by {@link #getUrl()}.
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj instanceof Stylesheet) {
            Stylesheet other = (Stylesheet)obj;

            if (this.url == null && other.url == null) {
                return true;
            } else if (this.url == null || other.url == null) {
                return false;
            } else {
                return this.url.equals(other.url);
            }
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override public int hashCode() {
        int hash = 7;
        hash = 13 * hash + (this.url != null ? this.url.hashCode() : 0);
        return hash;
    }

    /** Returns a string representation of this object. */
    @Override public String toString() {
        StringBuilder sbuf = new StringBuilder();
        sbuf.append("/* ");
        if (url != null) sbuf.append(url);
        if (rules.isEmpty()) {
            sbuf.append(" */");
        } else {
            sbuf.append(" */\n");
            for(int r=0; r fontFaceList = getFontFaces();
        int nFontFaces = fontFaceList != null ? fontFaceList.size() : 0;
        os.writeShort(nFontFaces);

        for(int n=0; n persistedRules = new ArrayList<>(nRules);
        for (int n=0; n= 5) {
            List fontFaceList = this.getFontFaces();
            int nFontFaces = is.readShort();
            for (int n=0; n Stylesheet.BINARY_CSS_VERSION) {
                throw new IOException(
                    String.format("Wrong binary CSS version %s, expected version less than or equal to %s",
                        uri != null ? bssVersion + " in stylesheet \"" + uri + "\"" : bssVersion,
                        Stylesheet.BINARY_CSS_VERSION));
            }
            // read strings
            final String[] strings = StringStore.readBinary(dataInputStream);
            // read binary data
            stylesheet = new Stylesheet(uri);

            try {

                dataInputStream.mark(Integer.MAX_VALUE);
                stylesheet.readBinary(bssVersion, dataInputStream, strings);

            } catch (Exception e) {

                stylesheet = new Stylesheet(uri);

                dataInputStream.reset();

                if (bssVersion == 2) {
                    // RT-31022
                    stylesheet.readBinary(3, dataInputStream, strings);
                } else {
                    stylesheet.readBinary(Stylesheet.BINARY_CSS_VERSION, dataInputStream, strings);
                }
            }

        }

        // return stylesheet
        return stylesheet;
    }

    /**
     * Converts the css file referenced by {@code source} to binary format and writes it to {@code destination}.
     *
     * @param source the JavaFX compliant css file to convert
     * @param destination the file to which the binary formatted data is written
     * @throws IOException if the destination file can not be created or if an I/O error occurs
     * @throws IllegalArgumentException if either parameter is {@code null}, if {@code source} and
     * {@code destination} are the same, if {@code source} cannot be read, or if {@code destination}
     * cannot be written
     */
    public static void convertToBinary(File source, File destination) throws IOException {

        if (source == null || destination == null) {
            throw new IllegalArgumentException("parameters may not be null");
        }

        if (source.getAbsolutePath().equals(destination.getAbsolutePath())) {
            throw new IllegalArgumentException("source and destination may not be the same");
        }

        if (source.canRead() == false) {
            throw new IllegalArgumentException("cannot read source file");
        }

        if (destination.exists() ? (destination.canWrite() == false) : (destination.createNewFile() == false)) {
            throw new IllegalArgumentException("cannot write destination file");
        }

        URI sourceURI = source.toURI();
        Stylesheet stylesheet = new CssParser().parse(sourceURI.toURL());

        // first write all the css binary data into the buffer and collect strings on way
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        StringStore stringStore = new StringStore();
        stylesheet.writeBinary(dos, stringStore);
        dos.flush();
        dos.close();

        FileOutputStream fos = new FileOutputStream(destination);
        DataOutputStream os = new DataOutputStream(fos);

        // write file version
        os.writeShort(BINARY_CSS_VERSION);

        // write strings
        stringStore.writeBinary(os);

        // write binary css
        os.write(baos.toByteArray());
        os.flush();
        os.close();
    }

    // Add the rules from the other stylesheet to this one
    void importStylesheet(Stylesheet importedStylesheet) {
        if (importedStylesheet == null) return;

        List rulesToImport = importedStylesheet.getRules();
        if (rulesToImport == null || rulesToImport.isEmpty()) return;

        List importedRules = new ArrayList<>(rulesToImport.size());
        for (Rule rule : rulesToImport) {
            List selectors = rule.getSelectors();
            List declarations = rule.getUnobservedDeclarationList();
            importedRules.add(new Rule(selectors, declarations));
        }

        rules.addAll(importedRules);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy