| /* |
| * 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; |
| } |
| } |