blob: 79fb947cacb534a964169594c5e79630a544564d [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.uiDesigner.compiler;
import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.lw.*;
import org.jdom.Document;
import org.jdom.input.SAXBuilder;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.swing.*;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
/**
* @author Anton Katilin
* @author Vladimir Kondratyev
* <p/>
* NOTE: the class must be compilable with JDK 1.3, so any methods and filds introduced in 1.4 or later must not be used
*/
public final class Utils {
public static final String FORM_NAMESPACE = "http://www.intellij.com/uidesigner/form/";
private static final SAXParser SAX_PARSER = createParser();
private Utils() {
}
private static SAXParser createParser() {
try {
return SAXParserFactory.newInstance().newSAXParser();
}
catch (Exception e) {
return null;
}
}
/**
* @param provider if null, no classes loaded and no properties read
*/
public static LwRootContainer getRootContainer(final String formFileContent, final PropertiesProvider provider) throws Exception {
if (formFileContent.indexOf(FORM_NAMESPACE) == -1) {
throw new AlienFormFileException();
}
final Document document = new SAXBuilder().build(new StringReader(formFileContent), "UTF-8");
return getRootContainerFromDocument(document, provider);
}
/**
* Get root from the url
*
* @param formFile the document URL
* @param provider the provider
* @return the root container
* @throws Exception if there is a problem with parsing DOM
*/
public static LwRootContainer getRootContainer(final URL formFile, final PropertiesProvider provider) throws Exception {
final Document document = new SAXBuilder().build(formFile);
return getRootContainerFromDocument(document, provider);
}
/**
* Get root from the document
*
* @param document the parsed document
* @param provider the provider
* @return the root container
* @throws Exception if there is a problem with parsing DOM
*/
private static LwRootContainer getRootContainerFromDocument(Document document, PropertiesProvider provider) throws Exception {
final LwRootContainer root = new LwRootContainer();
root.read(document.getRootElement(), provider);
return root;
}
public static LwRootContainer getRootContainer(final InputStream stream, final PropertiesProvider provider) throws Exception {
final Document document = new SAXBuilder().build(stream, "UTF-8");
return getRootContainerFromDocument(document, provider);
}
public synchronized static String getBoundClassName(final String formFileContent) throws Exception {
if (formFileContent.indexOf(FORM_NAMESPACE) == -1) {
throw new AlienFormFileException();
}
final String[] className = new String[]{null};
try {
SAX_PARSER.parse(new InputSource(new StringReader(formFileContent)), new DefaultHandler() {
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("form".equals(qName)) {
className[0] = attributes.getValue("", "bind-to-class");
throw new SAXException("stop parsing");
}
}
});
}
catch (Exception e) {
// Do nothing.
}
return className[0];
}
/**
* Validates that specified class represents {@link javax.swing.JComponent} with
* empty constructor.
*
* @return descriptive human readable error message or <code>null</code> if
* no errors were detected.
*/
public static String validateJComponentClass(final ClassLoader loader, final String className, final boolean validateConstructor) {
if (loader == null) {
throw new IllegalArgumentException("loader cannot be null");
}
if (className == null) {
throw new IllegalArgumentException("className cannot be null");
}
// These classes are not visible for passed class loader!
if ("com.intellij.uiDesigner.HSpacer".equals(className) || "com.intellij.uiDesigner.VSpacer".equals(className)) {
return null;
}
final Class aClass;
try {
aClass = Class.forName(className, false, loader);
}
catch (final ClassNotFoundException exc) {
return "Class \"" + className + "\"not found";
}
catch (NoClassDefFoundError exc) {
return "Cannot load class " + className + ": " + exc.getMessage();
}
catch (ExceptionInInitializerError exc) {
return "Cannot initialize class " + className + ": " + exc.getMessage();
}
catch (UnsupportedClassVersionError exc) {
return "Unsupported class version error: " + className;
}
if (validateConstructor) {
try {
final Constructor constructor = aClass.getConstructor(new Class[0]);
if ((constructor.getModifiers() & Modifier.PUBLIC) == 0) {
return "Class \"" + className + "\" does not have default public constructor";
}
}
catch (final Exception exc) {
return "Class \"" + className + "\" does not have default constructor";
}
}
// Check that JComponent is accessible via the loader
if (!JComponent.class.isAssignableFrom(aClass)) {
return "Class \"" + className + "\" is not an instance of javax.swing.JComponent";
}
return null;
}
public static void validateNestedFormLoop(final String formName, final NestedFormLoader nestedFormLoader)
throws CodeGenerationException, RecursiveFormNestingException {
validateNestedFormLoop(formName, nestedFormLoader, null);
}
public static void validateNestedFormLoop(final String formName, final NestedFormLoader nestedFormLoader, final String targetForm)
throws CodeGenerationException, RecursiveFormNestingException {
HashSet usedFormNames = new HashSet();
if (targetForm != null) {
usedFormNames.add(targetForm);
}
validateNestedFormLoop(usedFormNames, formName, nestedFormLoader);
}
private static void validateNestedFormLoop(final Set usedFormNames, final String formName, final NestedFormLoader nestedFormLoader)
throws CodeGenerationException, RecursiveFormNestingException {
if (usedFormNames.contains(formName)) {
throw new RecursiveFormNestingException();
}
usedFormNames.add(formName);
final LwRootContainer rootContainer;
try {
rootContainer = nestedFormLoader.loadForm(formName);
}
catch (Exception e) {
throw new CodeGenerationException(null, "Error loading nested form: " + e.getMessage(), e);
}
final Set thisFormNestedForms = new HashSet();
final CodeGenerationException[] validateExceptions = new CodeGenerationException[1];
final RecursiveFormNestingException[] recursiveNestingExceptions = new RecursiveFormNestingException[1];
rootContainer.accept(new ComponentVisitor() {
public boolean visit(final IComponent component) {
if (component instanceof LwNestedForm) {
LwNestedForm nestedForm = (LwNestedForm)component;
if (!thisFormNestedForms.contains(nestedForm.getFormFileName())) {
thisFormNestedForms.add(nestedForm.getFormFileName());
try {
validateNestedFormLoop(usedFormNames, nestedForm.getFormFileName(), nestedFormLoader);
}
catch (RecursiveFormNestingException e) {
recursiveNestingExceptions[0] = e;
return false;
}
catch (CodeGenerationException e) {
validateExceptions[0] = e;
return false;
}
}
}
return true;
}
});
if (recursiveNestingExceptions[0] != null) {
throw recursiveNestingExceptions[0];
}
if (validateExceptions[0] != null) {
throw validateExceptions[0];
}
}
public static String findNotEmptyPanelWithXYLayout(final IComponent component) {
if (!(component instanceof IContainer)) {
return null;
}
final IContainer container = (IContainer)component;
if (container.getComponentCount() == 0) {
return null;
}
if (container.isXY()) {
return container.getId();
}
for (int i = 0; i < container.getComponentCount(); i++) {
String id = findNotEmptyPanelWithXYLayout(container.getComponent(i));
if (id != null) {
return id;
}
}
return null;
}
public static int getHGap(LayoutManager layout) {
if (layout instanceof BorderLayout) {
return ((BorderLayout)layout).getHgap();
}
if (layout instanceof CardLayout) {
return ((CardLayout)layout).getHgap();
}
return 0;
}
public static int getVGap(LayoutManager layout) {
if (layout instanceof BorderLayout) {
return ((BorderLayout)layout).getVgap();
}
if (layout instanceof CardLayout) {
return ((CardLayout)layout).getVgap();
}
return 0;
}
public static int getCustomCreateComponentCount(final IContainer container) {
final int[] result = new int[1];
result[0] = 0;
container.accept(new ComponentVisitor() {
public boolean visit(IComponent c) {
if (c.isCustomCreate()) {
result[0]++;
}
return true;
}
});
return result[0];
}
public static Class suggestReplacementClass(Class componentClass) {
while (true) {
componentClass = componentClass.getSuperclass();
if (componentClass.equals(JComponent.class)) {
return JPanel.class;
}
if ((componentClass.getModifiers() & (Modifier.ABSTRACT | Modifier.PRIVATE)) != 0) {
continue;
}
try {
componentClass.getConstructor(new Class[]{});
}
catch (NoSuchMethodException ex) {
continue;
}
return componentClass;
}
}
public static InstrumentationClassFinder.PseudoClass suggestReplacementClass(InstrumentationClassFinder.PseudoClass componentClass) throws ClassNotFoundException, IOException {
final InstrumentationClassFinder.PseudoClass jComponentClass = componentClass.getFinder().loadClass(JComponent.class.getName());
while (true) {
componentClass = componentClass.getSuperClass();
if (componentClass.equals(jComponentClass)) {
return componentClass.getFinder().loadClass(JPanel.class.getName());
}
if ((componentClass.getModifiers() & (Modifier.ABSTRACT | Modifier.PRIVATE)) != 0) {
continue;
}
if (!componentClass.hasDefaultPublicConstructor()) {
continue;
}
return componentClass;
}
}
public static int alignFromConstraints(final GridConstraints gc, final boolean horizontal) {
int anchor = gc.getAnchor();
int fill = gc.getFill();
int leftMask = horizontal ? GridConstraints.ANCHOR_WEST : GridConstraints.ANCHOR_NORTH;
int rightMask = horizontal ? GridConstraints.ANCHOR_EAST : GridConstraints.ANCHOR_SOUTH;
int fillMask = horizontal ? GridConstraints.FILL_HORIZONTAL : GridConstraints.FILL_VERTICAL;
if ((fill & fillMask) != 0) return GridConstraints.ALIGN_FILL;
if ((anchor & rightMask) != 0) return GridConstraints.ALIGN_RIGHT;
if ((anchor & leftMask) != 0) return GridConstraints.ALIGN_LEFT;
return GridConstraints.ALIGN_CENTER;
}
public static boolean isBoundField(IComponent component, String fieldName) {
if (fieldName.equals(component.getBinding())) {
return true;
}
if (component instanceof IContainer) {
IContainer container = (IContainer)component;
for (int i = 0; i < container.getComponentCount(); i++) {
if (isBoundField(container.getComponent(i), fieldName)) {
return true;
}
}
}
return false;
}
}