📄 fxobjectstylesupport.java
字号:
/*
* Copyright 2008 Sun Microsystems, Inc. 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.
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.stylesheet.styleable;
import com.sun.stylesheet.Styleable;
import com.sun.stylesheet.StylesheetException;
import com.sun.stylesheet.PseudoclassListener;
import com.sun.stylesheet.types.TypeManager;
import com.sun.javafx.runtime.FXObject;
import com.sun.javafx.runtime.location.AbstractVariable;
import com.sun.javafx.runtime.location.BooleanVariable;
import com.sun.javafx.runtime.location.ChangeListener;
import com.sun.javafx.runtime.location.DoubleVariable;
import com.sun.javafx.runtime.location.IntVariable;
import com.sun.javafx.runtime.location.SequenceVariable;
import com.sun.javafx.runtime.sequence.Sequence;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>An intermediate implementation of FXObjectStyleSupport based on reverse
* engineered reflective access to the generated class files. This may break.
* This needs to be rewritten based on javafx reflection when possible. Some
* things here may not work.</p>
*
* <p>The FX script compiler generates a great number of class files from
* a single FX script class. Each bound function ends up generating an inner
* class. Each trigger implementation also (I think) generates an inner class.
* Each attribute is represented by an AbstractVariable (as opposed to generating
* getter and setter methods instead). Oddly, a generated file has both a public
* final field for each attribute, and then a get$attrname method for each
* attribute. Both have the same values(?? weird).</p>
*
* <p>Due to multiple inheritance support in fx-script, the compiler actually
* ends up flattening the inheritance heirarchy and preserving the inheritance
* graph in interfaces. (I think the get$attrname methods are used for
* preserving the attribute names available in the inheritance graph).</p>
*
* <p>Given a javafx class Foo, two files will be generated: Foo.class and
* Foo$Impl.class. The Foo.class contains ALL the implementation for that
* class, whereas Foo$Impl.class preserves the API defined in javafx class Foo.</p>
*
* <p>Given a javafx class Baz that extends both a javafx class Foo and a
* javafx class Bar, the Baz class files will include Baz.class and Baz$Intf.class.
* Baz$Intf.class will extend both Bar$Intf and Foo$Intf. Baz.class there implements
* all of Foo$Intf, Bar$Intf, and Baz$Intf. However, Baz.class contains ALL the
* implementation for all three of these classes. This is done by essentially
* copy & paste from Bar.class and Foo.class into Bar.class (plus the Bar
* implementation).</p>
*
* <p>This particular implementation is focused squarely on Reprise and the
* new Node-based Controls. Note that Node-based Controls are not part of publicly
* available API and thus this class cannot be committed as is to the javacss
* project.</p>
*
* <p>Currently Nodes and Reprise Components are two different things. When
* they are successfully merged, this class will need to be fixed.</p>
*
* @author Richard Bair
*/
public class FXObjectStyleSupport implements StyleSupport {
/**
* All of the implementation classes that are part of the inheritance
* heirarchy.... Hmm.
*/
private Class[] objectClasses = new Class[0];
/**
* The class that this FXObjectStyleSupport is intended to support. For
* example, this could be javafx.gui.Button.
*/
private Class cls;
private boolean isControl;
private boolean isNode;
private boolean isCanvas;
private boolean isComponent;
private boolean isGroup;
private boolean isContainer;
private Map<String, Attribute> attributes = new HashMap<String, Attribute>();
/**
* Create an FXObjectStyleSupport for an FXObject class. All fx-script classes
* are compiled to classes that extend Object and implement FXObject.
*
* @param cls
*/
public FXObjectStyleSupport(Class cls) {
if (!FXObject.class.isAssignableFrom(cls)) {
throw new IllegalArgumentException("Illegal attempt to use " +
"FXObjectStyleSupport with a non-fx class");
}
this.cls = cls;
try {
Class intf = Class.forName("javafx.gui.Node$Intf");
isNode = intf.isAssignableFrom(cls);
intf = Class.forName("javafx.gui.Group$Intf");
isGroup = intf.isAssignableFrom(cls);
intf = Class.forName("javafx.gui.Control$Intf");
isControl = intf.isAssignableFrom(cls);
intf = Class.forName("javafx.gui.Canvas$Intf");
isCanvas = intf.isAssignableFrom(cls);
intf = Class.forName("javafx.gui.swing.Component$Intf");
isComponent = intf.isAssignableFrom(cls);
intf = Class.forName("javafx.gui.swing.Container$Intf");
isContainer = intf.isAssignableFrom(cls);
} catch (Exception e) {
throw new IllegalStateException("Failed to load necessary " +
"FX classes. They may not be on the classpath", e);
}
// pre-determine the class hierarchy here
// All JavaFX classes that do not extend Java classes directly
// end up extending java.lang.Object directly.
// Their "fx inheritance" is determined actually by the interfaces
// that the class implements and inherits from. Each "fx class" is
// represented as Foo.class and Foo$Intf.class. If the javafx class
// Foo extends Bar, then there will be an
// interface Foo$Intf extends Bar$Intf
// get the $Intf interface associated with this class
Class intf = null;
Class[] interfaces = cls.getInterfaces();
for (Class c : interfaces) {
if (c.getName().equals(cls.getName() + "$Intf")) {
intf = c;
break;
}
}
if (intf == null) {
throw new IllegalStateException("Failed to find the $Intf class " +
"for " + cls.getName());
}
if (cls.getSuperclass() != Object.class) {
// I have an FX class which extends a traditional Java class.
// In this case, I don't have to worry about figuring out
// the inheritance graph, so I can go ahead and just set the
// object classes to be this class and its interfaces.
objectClasses = new Class[interfaces.length + 1];
objectClasses[0] = cls;
System.arraycopy(interfaces, 0, objectClasses, 1, interfaces.length);
} else {
// I have an FX class that extends another FX class. I have to
// therefore construct my objectClasses to contain the implementation
// classes for all of the $Intf interfaces that are implemented
Set<Class> classes = new HashSet<Class>();
try {
accumulateClasses(cls, classes);
objectClasses = classes.toArray(new Class[classes.size()]);
} catch (Exception e) {
throw new IllegalStateException("Failed to load the class " +
"heirarchy for " + cls.getName(), e);
}
}
}
private void accumulateClasses(Class cls, Set<Class> classes) throws Exception {
classes.add(cls);
for (Class c : cls.getInterfaces()) {
String name = c.getName();
if (name.endsWith("$Intf")) {
// must have found an FX class interface. Find the corresponding
// concrete implementation class and include that
ClassLoader loader = c.getClassLoader();
Class concrete = Class.forName(name.substring(0, name.length() - 5), false, loader);
classes.add(concrete);
accumulateClasses(c, classes);
}
}
}
/**
* All Nodes have an "id" attribute. Component has a "name" attribute. Both
* of these act as the "id". If a class happens to extend both Node and
* Component, then Node's id is honored and "name" is ignored.
*
* @param object
* @return
*/
public String getID(Object object) {
Object id = null;
if (isNode) {
id = getAttribute("id").getProperty(object);
} else if (isComponent) {
id = getAttribute("name").getProperty(object);
}
return id == null ? null : id.toString();
}
/**
* Only controls currently have a styleClass attribute, so only they
* return a non-null value from this method.
*
* @param object
* @return
*/
public String getStyleClass(Object object) {
if (isControl) {
Object styleClass = getAttribute("styleClass").getProperty(object);
return styleClass == null ? null : styleClass.toString();
}
return null;
}
public Class[] getObjectClasses(Object object) {
return objectClasses;
}
/**
* If the given object is a Node, then we find the parent node and wrap it
* as a Styleable. If the given object is a Component, then we find the
* parent component and wrap it as a Styleable.
*
* @param object
* @return
*/
public Styleable getStyleableParent(Object object) {
if (isNode || isComponent) {
Object parent = getAttribute("parent").getProperty(object);
return parent == null ? null : TypeManager.getStyleable(parent);
}
return null;
}
/**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -