Package com.google.gdt.eclipse.designer.model.widgets.support

Source Code of com.google.gdt.eclipse.designer.model.widgets.support.GwtState$ResourceProvider

/*******************************************************************************
* Copyright 2011 Google Inc. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.google.gdt.eclipse.designer.model.widgets.support;

import com.google.common.collect.Lists;
import com.google.gdt.eclipse.designer.Activator;
import com.google.gdt.eclipse.designer.IExceptionConstants;
import com.google.gdt.eclipse.designer.common.Constants;
import com.google.gdt.eclipse.designer.hosted.IBrowserShell;
import com.google.gdt.eclipse.designer.hosted.IHostedModeSupport;
import com.google.gdt.eclipse.designer.hosted.IHostedModeSupportFactory;
import com.google.gdt.eclipse.designer.model.module.ModuleElement;
import com.google.gdt.eclipse.designer.model.module.PropertyProviderElement;
import com.google.gdt.eclipse.designer.support.http.HttpServer;
import com.google.gdt.eclipse.designer.support.http.IModuleInitializer;
import com.google.gdt.eclipse.designer.support.http.IResourceProvider;
import com.google.gdt.eclipse.designer.util.ModuleDescription;
import com.google.gdt.eclipse.designer.util.ModuleVisitor;
import com.google.gdt.eclipse.designer.util.Utils;
import com.google.gdt.eclipse.designer.util.resources.IResourcesProvider;

import org.eclipse.wb.core.editor.IDesignPageSite;
import org.eclipse.wb.draw2d.geometry.Dimension;
import org.eclipse.wb.draw2d.geometry.Insets;
import org.eclipse.wb.draw2d.geometry.Point;
import org.eclipse.wb.draw2d.geometry.Rectangle;
import org.eclipse.wb.internal.core.DesignerPlugin;
import org.eclipse.wb.internal.core.utils.IOUtils2;
import org.eclipse.wb.internal.core.utils.Version;
import org.eclipse.wb.internal.core.utils.exception.DesignerException;
import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils;
import org.eclipse.wb.internal.core.utils.execution.RunnableEx;
import org.eclipse.wb.internal.core.utils.external.ExternalFactoriesHelper;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;

/**
* Provides access to the GWT module, hosted mode {@link ClassLoader} and many other low level
* things.
*
* @author mitin_aa
* @author scheglov_ke
* @coverage gwt.model
*/
public final class GwtState {
  private static final String CLASSPATH_URL_MARKER = "/__classpath__/";
  private static final String START_HTML = "__start.html";
  // errors for last state
  private static String m_loggerErrorMessages;
  // for tests
  public static final List<GwtState> INSTANCES = Lists.newArrayList();
  public final String m_testQualifiedName;
  // from constructor
  private final ClassLoader m_parentClassLoader;
  private final Version m_version;
  private final IJavaProject m_javaProject;
  private final ModuleDescription m_moduleDescription;
  private String m_moduleId;
  private String m_moduleName;
  private final CssSupport m_cssSupport = new CssSupport(this);
  // HTML
  private static int m_nextStateIndex = 0;
  protected final String m_moduleBaseURL = "/" + m_nextStateIndex++ + "/";
  private final String m_startHtmlUrl = m_moduleBaseURL + START_HTML;
  private boolean m_strictMode;
  private String m_html;
  // hosted mode
  private boolean m_initialized;
  private IBrowserShell m_shell;
  private IHostedModeSupport m_hostModeSupport;
  private final DOMUtils m_domUtils = new DOMUtils(this);
  private final UIObjectUtils m_uiObjectUtils = new UIObjectUtils(this);
  private Object m_body;
  ////////////////////////////////////////////////////////////////////////////
  //
  // IDevModeBridge
  //
  ////////////////////////////////////////////////////////////////////////////
  private final IDevModeBridge m_devModeBridge = new IDevModeBridge() {
    public ClassLoader getDevClassLoader() {
      return m_hostModeSupport.getDevClassLoader();
    }

    public Object findJType(String name) {
      return m_hostModeSupport.findJType(name);
    }

    public void invalidateRebind(String binderClassName) {
      m_hostModeSupport.invalidateRebind(binderClassName);
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  //
  // Constructor
  //
  ////////////////////////////////////////////////////////////////////////////
  public GwtState(ClassLoader parentClassLoader, ModuleDescription moduleDescription)
      throws Exception {
    // remember module
    m_moduleDescription = moduleDescription;
    if (m_moduleDescription == null) {
      throw new DesignerException(IExceptionConstants.NO_MODULE_FILE);
    }
    // remember arguments
    m_parentClassLoader = parentClassLoader;
    m_javaProject = JavaCore.create(m_moduleDescription.getProject());
    // remember for access from other classes
    INSTANCES.add(this);
    m_testQualifiedName = findTestQualifiedName();
    // safe operation
    m_version = Utils.getVersion(m_javaProject);
  }

  /**
   * Prepares module, HTML and CSS information, then initializes {@link IHostedModeSupport}.
   */
  public void initialize() throws Exception {
    // prepare module
    {
      m_moduleId = m_moduleDescription.getId();
      m_moduleName = Utils.readModule(m_moduleDescription).getName();
    }
    // prepare HTML text
    {
      InputStream is = GwtState.class.getResourceAsStream(START_HTML);
      if (is == null) {
        throw new RuntimeException("Unable to load initialization page.");
      }
      try {
        m_html = IOUtils2.readString(is);
      } finally {
        IOUtils.closeQuietly(is);
      }
      // add "docType"
      {
        String docType = Utils.getDocType(m_moduleDescription);
        // null means no HTML, defaulting to strict mode
        if (docType == null) {
          m_strictMode = true;
          m_html = "<!doctype html>\n" + m_html;
        } else {
          m_strictMode =
              docType.equalsIgnoreCase("<!doctype html>")
                  || docType.equalsIgnoreCase("<!doctype html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">")
                  || docType.equalsIgnoreCase("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
          if (!StringUtils.isEmpty(docType)) {
            m_html = docType + "\n" + m_html;
          }
        }
      }
      // set any declarations
      {
        String declarations = getDeclarations();
        m_html = StringUtils.replace(m_html, "%CSS_DECLARATIONS%", declarations);
      }
      // set inline variables
      {
        m_html = StringUtils.replace(m_html, "%MODULE_ID%", m_moduleId);
        m_html = StringUtils.replace(m_html, "%MODULE_NAME%", m_moduleName);
        m_html = StringUtils.replace(m_html, "%MODULE_BASE%", m_moduleBaseURL);
        m_html = StringUtils.replace(m_html, "%GWT_VERSION%", m_version.getStringMajorMinor());
      }
      // set default "locale"
      {
        String defaultLocale = Utils.getDefaultLocale(m_moduleDescription);
        m_html = StringUtils.replace(m_html, "%GWT_LOCALE%", defaultLocale);
      }
      // updates HTML file to support CSS files reloading
      m_html = m_cssSupport.addReloadingFeature(m_html);
    }
    // install resource provider
    HttpServer.getInstance().addResourceProvider(m_moduleBaseURL, new ResourceProvider());
    // perform initialize (long running operation)
    {
      IProgressMonitor monitor = IDesignPageSite.Helper.getProgressMonitor();
      initialize0(monitor);
    }
    // set active
    activate();
  }

  /**
   * Extracts from stack trace name of class and method of test which creates this {@link GwtState}.
   */
  private static String findTestQualifiedName() {
    for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
      String methodName = element.getMethodName();
      if (methodName.startsWith("test_")) {
        return element.getClassName() + "." + methodName;
      }
    }
    return "noTestMethod";
  }

  /**
   * Initializes {@link IHostedModeSupport} and this {@link GwtState} class.
   */
  private void initialize0(IProgressMonitor monitor) throws Exception {
    monitor.subTask("Initializing GWT Development Mode...");
    m_hostModeSupport = getHostedModeSupport();
    {
      // get shell
      m_shell = m_hostModeSupport.getBrowserShell();
      // set shell size
      m_shell.setSize(450, 300);
    }
    {
      // prepare user-agent
      m_html = StringUtils.replace(m_html, "%USER_AGENT%", m_shell.getUserAgentString());
      m_html =
          StringUtils.replace(
              m_html,
              "%PROPERTY_PROVIDER_SCRIPTS%",
              getPropertyProviderValuesScript());
      m_html = StringUtils.replace(m_html, "%GWT_isBrowserExplorer%", "" + isBrowserExplorer());
    }
    // prepare hosted mode
    m_hostModeSupport.startup(getBrowserStartupUrl(), m_moduleId, monitor, getHostedModeTimeout());
    m_initialized = true;
    // configure Window
    m_body = m_uiObjectUtils.getRootPanelElement();
    ReflectionUtils.invokeMethod(
        m_uiObjectUtils.getClassOfWindow(),
        "enableScrolling(boolean)",
        false);
  }

  /**
   * @return the script for assigning property values into "values" map, declared in "__start.html".
   */
  private String getPropertyProviderValuesScript() throws Exception {
    final StringBuilder sb = new StringBuilder();
    ModuleVisitor.accept(m_moduleDescription, new ModuleVisitor() {
      @Override
      public void endVisitModule(ModuleElement module) {
        for (PropertyProviderElement provider : module.getPropertyProviderElements()) {
          String script = provider.getScript();
          if (script != null) {
            sb.append("values['" + provider.getName() + "'] = function() {" + script + "} ();\n");
          }
        }
      }
    });
    return sb.toString();
  }

  /**
   * @return the timeout to wait for hosted mode to initialize.
   */
  private static int getHostedModeTimeout() {
    IPreferenceStore store = Activator.getStore();
    int timeout = store.getInt(Constants.P_GWT_HOSTED_INIT_TIME);
    if (timeout == 0) {
      return Integer.MAX_VALUE;
    }
    return timeout * 1000;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Browser support methods
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Creates a screenshot image of Browser control. This method waits for any pending images to load
   * and CSS loading Browser operations to complete, then calls native part to create the image.
   */
  public Image createBrowserScreenshot() throws Exception {
    // wait for possibly updated CSS files to be applied
    m_cssSupport.waitFor();
    // waiting for browser to download all images
    waitForImages();
    // do screen shot
    return m_shell.createBrowserScreenshot();
  }

  /**
   * Forces an outstanding messages to be processed
   */
  public void runMessagesLoop() {
    Runnable enableEventsRunnable = disableMouseAndKeyboard();
    try {
      while (Display.getCurrent().readAndDispatch()) {
        // wait
      }
    } catch (Throwable e) {
    } finally {
      enableEventsRunnable.run();
    }
  }

  /**
   * We should process messages while waiting Browser to load all images, style, etc. However this
   * means that user may perform some action, such as trying to change properties, move components,
   * etc. During execution of command. So, we should catch dangerous events and ignore them.
   * <p>
   * http://fogbugz.instantiations.com/fogbugz/default.php?43606
   */
  private Runnable disableMouseAndKeyboard() {
    final int[] events = {SWT.MouseDown, SWT.MouseUp, SWT.MouseDoubleClick, SWT.KeyDown, SWT.KeyUp};
    final Display display = DesignerPlugin.getStandardDisplay();
    final Listener listener = new Listener() {
      public void handleEvent(Event event) {
        event.doit = false;
        event.type = SWT.NONE;
      }
    };
    for (int i = 0; i < events.length; i++) {
      display.addFilter(events[i], listener);
    }
    return new Runnable() {
      public void run() {
        for (int i = 0; i < events.length; i++) {
          display.removeFilter(events[i], listener);
        }
      }
    };
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Access
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * @return the internal {@link IHostedModeSupport}.
   */
  IHostedModeSupport getHostModeSupport() {
    return m_hostModeSupport;
  }

  /**
   * @return the [ERROR] entries passed into GWT log, as string. Returns "&lt;none&gt;" if no such
   *         messages. Only entries of last disposed {@link GwtState} are returned, because this is
   *         what we need during exception rewriting.
   */
  public static String getLoggerErrorMessages() {
    return m_loggerErrorMessages;
  }

  /**
   * @return the {@link UIObjectUtils} to work with <code>UIObject</code>.
   */
  public UIObjectUtils getUIObjectUtils() {
    return m_uiObjectUtils;
  }

  /**
   * @return the {@link DOMUtils} to work with <code>Element</code>.
   */
  public DOMUtils getDomUtils() {
    return m_domUtils;
  }

  public IBrowserShell getShell() {
    return m_shell;
  }

  public IDevModeBridge getDevModeBridge() {
    return m_devModeBridge;
  }

  /**
   * @return the string to set as browser start URL.
   */
  private String getBrowserStartupUrl() {
    return "http://" + HttpServer.getInstance().getTCPAddress() + m_startHtmlUrl;
  }

  /**
   * @return the {@link ModuleDescription}.
   */
  public ModuleDescription getModuleDescription() {
    return m_moduleDescription;
  }

  public String getModuleName() {
    return m_moduleId;
  }

  public Version getVersion() {
    return m_version;
  }

  /**
   * @return <code>true</code> if "strict" mode is using, so for example CSS tricks work as
   *         expected.
   */
  public boolean isStrictMode() {
    return m_strictMode;
  }

  /**
   * @return <code>true</code> if browser is the "Internet Explorer".
   */
  public boolean isBrowserExplorer() {
    String agent = m_shell.getUserAgentString();
    return "ie6".equals(agent) || "ie7".equals(agent) || "ie8".equals(agent) || "ie9".equals(agent);
  }

  /**
   * @return <code>true</code> if browser is based on WebKit.
   */
  public boolean isBrowserWebKit() {
    String agent = m_shell.getUserAgentString();
    return "safari".equals(agent);
  }

  /**
   * @return the {@link CssSupport} for CSS operations.
   */
  public CssSupport getCssSupport() {
    return m_cssSupport;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // SWT Shell wrapping methods
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Create platform-dependent shell with browser.
   *
   * @return IBrowserShell instance
   */
  public void showShell() {
    m_shell.showAsPreview();
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Images
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * When we use images, browser requests images in background, so we should wait until all (local)
   * images are loaded before making screen shot. Note: waits no more than 500ms.
   */
  private void waitForImages() throws Exception {
    // System.out.println("*** start IMAGE wait");
    long startWait = System.currentTimeMillis();
    while (true) {
      boolean complete =
          m_hostModeSupport.invokeNativeBoolean(
              "__waitForImages",
              ArrayUtils.EMPTY_CLASS_ARRAY,
              ArrayUtils.EMPTY_OBJECT_ARRAY);
      if (complete) {
        break;
      }
      // do not wait more than 500ms
      if (System.currentTimeMillis() - startWait > 500) {
        break;
      }
      // wait more
      runMessagesLoop();
    }
    // System.out.println("*** waiting for IMAGE done in " + (System.currentTimeMillis() - startWait) + "ms");
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // CSS
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Prepares declarations (CSS, Script & etc) that should be placed in HTML template. Also
   * remembers used CSS files to check them later for update.
   */
  private String getDeclarations() throws Exception {
    List<String> declarations = Lists.newArrayList();
    // prepare CSS declarations
    {
      m_cssSupport.prepareResources();
      m_cssSupport.addLinkDeclarations(declarations);
    }
    // other declarations
    {
      List<IModuleInitializer> initializers =
          ExternalFactoriesHelper.getElementsInstances(
              IModuleInitializer.class,
              "com.google.gdt.eclipse.designer.moduleInitializers",
              "initializer");
      for (IModuleInitializer moduleInitializer : initializers) {
        moduleInitializer.configure(m_moduleDescription, declarations);
      }
    }
    // final result
    return StringUtils.join(declarations, "\n");
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Sharing
  //
  ////////////////////////////////////////////////////////////////////////////
  private boolean m_shared;

  /**
   * @return <code>true</code> if this {@link GwtState} is shared, to speed up testing.
   */
  public boolean isShared() {
    return m_shared;
  }

  /**
   * Marks if this {@link GwtState} is shared or not.
   */
  public void setShared(boolean shared) {
    m_shared = shared;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Life cycle
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * @return <code>true</code> if this GWT session was already disposed.
   */
  public boolean isDisposed() {
    return m_shell == null || m_shell.isDisposed();
  }

  /**
   * Disposes any resources associated with this GWT session.
   */
  public void dispose() {
    // don't dispose if shared
    if (m_shared) {
      return;
    }
    // remember errors
    if (m_hostModeSupport != null) {
      m_loggerErrorMessages = m_hostModeSupport.getLogSupport().getErrorMessages();
    } else {
      m_loggerErrorMessages = "No hosted mode.";
    }
    // try to dispose
    if (!isDisposed()) {
      ExecutionUtils.runLog(new RunnableEx() {
        public void run() throws Exception {
          disposeWindowClass();
          HttpServer.getInstance().removeResourceProvider(m_moduleBaseURL);
          m_hostModeSupport.dispose();
        }
      });
    }
    // disposed in any case
    INSTANCES.remove(this);
  }

  /**
   * Disposes top level GWT <code>Window</code> class.
   */
  private void disposeWindowClass() throws Exception {
    if (!m_initialized) {
      return;
    }
    // outstanding dispatch events spawned just after the browser shell is disposed.
    // the workaround is to call onClosed event handler before disposing and...
    Class<?> classOfWindow = m_uiObjectUtils.getClassOfWindow();
    ReflectionUtils.invokeMethod(classOfWindow, "onClosed()");
    // prevent fire again
    // < 1.6 way
    {
      // used Vector class prior to 1.4 and ArrayList class for 1.4,
      // so use reflection to invoke 'clear()'
      Field closingListenersField =
          ReflectionUtils.getFieldByName(classOfWindow, "closingListeners");
      if (closingListenersField != null) {
        Object closingListeners = ReflectionUtils.getFieldObject(classOfWindow, "closingListeners");
        // ...clear listeners
        ReflectionUtils.invokeMethod(closingListeners, "clear()");
      }
    }
    // 1.6 way: disable listeners fire
    {
      Field handlersInitedField =
          ReflectionUtils.getFieldByName(classOfWindow, "closeHandlersInitialized");
      if (handlersInitedField != null) {
        ReflectionUtils.setField(classOfWindow, "closeHandlersInitialized", false);
      }
    }
    // remove window listeners to prevent events firing when the editor closed
    m_hostModeSupport.invokeNativeVoid(
        "__wbp_cleanupEvents",
        ArrayUtils.EMPTY_CLASS_ARRAY,
        ArrayUtils.EMPTY_OBJECT_ARRAY);
  }

  /**
   * Invoked when the context changed (ex., editor switched).
   */
  public void activate() throws Exception {
    m_hostModeSupport.activate();
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Modify check
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Checks if some external files related with this GWT state where modified. For example we check
   * CSS files.
   */
  public boolean isModified() {
    return m_cssSupport.isModified();
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Class loader
  //
  ////////////////////////////////////////////////////////////////////////////
  private IResourcesProvider m_resourcesProvider;

  public ClassLoader getClassLoader() {
    return m_hostModeSupport.getClassLoader();
  }

  public IResourcesProvider getResourcesProvider() throws Exception {
    if (m_resourcesProvider == null) {
      m_resourcesProvider = m_moduleDescription.getResourcesProvider();
    }
    return m_resourcesProvider;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Coordinates
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * @return the bounds of <code>Element</code> such that using them for
   *         <code>AbsolutePanel.setWidgetPosition()</code> will place this element/widget into same
   *         location.
   */
  public Rectangle getModelBounds(Object element) {
    if (element == null) {
      return new Rectangle();
    }
    Dimension size = getUIObjectSize(element);
    // for BODY we assume (0,0) as top-left
    if (isBody(element)) {
      return new Rectangle(new Point(0, 0), size);
    }
    // for any inner element
    {
      Insets margins = getMargins(element);
      int x = m_domUtils.getIntAttribute(element, "offsetLeft") - margins.left;
      int y = m_domUtils.getIntAttribute(element, "offsetTop") - margins.top;
      return new Rectangle(x, y, size.width + margins.getWidth(), size.height + margins.getHeight());
    }
  }

  /**
   * Returns absolute bounds for given element. Meaning of "absolute" is following: relative to
   * point (0,0) of screen shot that we show on design canvas. These bounds include also margins of
   * element.
   */
  public Rectangle getAbsoluteBounds(Object element) {
    if (element == null) {
      return new Rectangle();
    }
    Dimension size = getUIObjectSize(element);
    // for BODY we assume (0,0) as top-left
    Insets margins = getMargins(element);
    if (isBody(element)) {
      int x = 0;
      int y = 0;
      if (isStrictMode()) {
        size.width += margins.getWidth() + getBorders(element).getWidth();
        size.height += margins.getHeight() + getBorders(element).getHeight();
      }
      return new Rectangle(new Point(x, y), size);
    }
    // for any inner element
    {
      int x = m_domUtils.getAbsoluteLeft(element);
      int y = m_domUtils.getAbsoluteTop(element);
      // apply margins
      {
        x -= margins.left;
        y -= margins.top;
        size.width += margins.getWidth();
        size.height += margins.getHeight();
      }
      // bounds Rectangle
      return new Rectangle(x, y, size.width, size.height);
    }
  }

  /**
   * @return <code>true</code> if the given element represents BODY element.
   */
  public boolean isBody(Object element) {
    return element == m_body;
  }

  /**
   * @return the size of given element.
   */
  private Dimension getUIObjectSize(Object element) {
    int width;
    int height;
    if (isBody(element)) {
      if (isBrowserExplorer()) {
        // Kosta_20080728: at least in GWT 1.5 and IE6 we have to use "offsetXXX" even for BODY,
        //   because in other case we don't see full borders, see "Border for GWT browser" in GMail
        width = m_domUtils.getIntAttribute(element, "offsetWidth");
        height = m_domUtils.getIntAttribute(element, "offsetHeight");
      } else {
        // the size of RootPanel should be fetched from
        // "clientWidth/clientHeight" attributes because by standards
        // using "offsetXXX" attributes we get sizes only for BODY element
        // (Mozilla, IE in DOCTYPE mode)
        // see http://www.quirksmode.org/js/doctypes.html for details
        width = m_domUtils.getIntAttribute(element, "clientWidth");
        height = m_domUtils.getIntAttribute(element, "clientHeight");
      }
    } else {
      width = m_domUtils.getIntAttribute(element, "offsetWidth");
      height = m_domUtils.getIntAttribute(element, "offsetHeight");
    }
    return new Dimension(width, height);
  }

  public Insets getBorders(Object element) {
    int top = getBorderSideWidth(element, "top");
    int left = getBorderSideWidth(element, "left");
    int bottom = getBorderSideWidth(element, "bottom");
    int right = getBorderSideWidth(element, "right");
    return new Insets(top, left, bottom, right);
  }

  private int getBorderSideWidth(Object element, String name) {
    String style = getComputedStyle(element, "border-" + name + "-style");
    if (style == null || "none".equals(style)) {
      return 0;
    }
    return getComputedStylePx(element, "border-" + name + "-width");
  }

  public Insets getMargins(Object element) {
    String oldDisplayStyleValue = null;
    try {
      if (isBrowserWebKit()) {
        // Bug/Feature in WebKit: if the position of the element is
        // not absolute and there is a free space right to the element
        // then this space added to element's space as 'margin-right' computed style value.
        // The workaround is to set 'display' style to 'none' while measuring margins.
        // Refs:
        // http://fogbugz.instantiations.com/fogbugz/default.asp?45380
        // https://bugs.webkit.org/show_bug.cgi?id=13343
        Object parent = m_domUtils.getParent(element);
        String parentTag = m_domUtils.getTagName(parent);
        if (!"TD".equals(parentTag)) {
          // This workaround causes problem with "top" position of the element in "TD" - it
          // gets shifted by some value, something like half of TD height. Probably because of
          // using the default "center" vertical alignment. So, we should not touch children of TD.
          oldDisplayStyleValue = m_domUtils.getStyleAttribute(element, "display");
          m_domUtils.setStyleAttribute(element, "display", "none");
        }
      }
      int top = getComputedStylePx(element, "margin-top");
      int left = getComputedStylePx(element, "margin-left");
      int bottom = getComputedStylePx(element, "margin-bottom");
      int right = getComputedStylePx(element, "margin-right");
      return new Insets(top, left, bottom, right);
    } finally {
      if (oldDisplayStyleValue != null) {
        // restore display style
        m_domUtils.setStyleAttribute(element, "display", oldDisplayStyleValue);
      }
    }
  }

  public Insets getPaddings(Object element) {
    int top = getComputedStylePx(element, "padding-top");
    int left = getComputedStylePx(element, "padding-left");
    int bottom = getComputedStylePx(element, "padding-bottom");
    int right = getComputedStylePx(element, "padding-right");
    return new Insets(top, left, bottom, right);
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Style
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * @return the attribute of computed style.
   */
  public String getComputedStyle(Object element, String style) {
    return m_hostModeSupport.invokeNativeString(
        "__getStyle",
        new Class[]{m_uiObjectUtils.getClassOfElement(), String.class},
        new Object[]{element, style});
  }

  /**
   * @return the value of integer attribute of computed style.
   */
  private int getComputedStylePx(Object element, String style) {
    String styleString = getComputedStyle(element, style);
    return getValuePx(styleString);
  }

  /**
   * @return the number of pixels from given size style string.
   */
  public static int getValuePx(String styleString) {
    if (styleString != null && styleString.endsWith("px") /*"0px"*/) {
      styleString = StringUtils.removeEnd(styleString, "px");
      try {
        return (int) Double.parseDouble(styleString);
      } catch (NumberFormatException e) {
        return 0;
      }
    }
    // no style set or browser can't return it for designer
    return 0;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Visiting
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Visits public folders of given module and inherited modules (recursively).
   */
  public void accept(ModuleVisitor visitor) throws Exception {
    ModuleVisitor.accept(m_moduleDescription, visitor);
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // RequestHandler
  //
  ////////////////////////////////////////////////////////////////////////////
  private class ResourceProvider implements IResourceProvider {
    /**
     * Returns public resource with given path.
     *
     * Here "public" means resource placed in folders defined as public in module descriptors.
     */
    public byte[] getResource(String requestPath) {
      String resourcePath = StringUtils.substringBefore(requestPath, "?");
      // System.out.println(System.currentTimeMillis() + ": requestPath: " + requestPath);
      // resources from classpath
      {
        int index = requestPath.indexOf(CLASSPATH_URL_MARKER);
        if (index != -1) {
          String classpathPath = requestPath.substring(index + CLASSPATH_URL_MARKER.length());
          try {
            return IOUtils2.readBytes(getResourcesProvider().getResourceAsStream(classpathPath));
          } catch (Throwable e) {
            return null;
          }
        }
      }
      // HTML
      if (m_startHtmlUrl.equals(resourcePath)) {
        return m_html.getBytes();
      }
      // prepare "public" resource path
      String publicResourcePath = StringUtils.removeStart(resourcePath, m_moduleBaseURL);
      /*synchronized (System.out) {
        System.out.println("publicResourcePath: " + publicResourcePath);
        System.out.flush();
      }*/
      // may be CSS "apply wait" found
      {
        byte[] result = m_cssSupport.getResourceWait(publicResourcePath);
        if (result != null) {
          return result;
        }
      }
      // load resource
      try {
        byte[] result = null;
        // load static resource
        {
          InputStream is = Utils.getResource(m_moduleDescription, publicResourcePath);
          if (is != null) {
            result = IOUtils2.readBytes(is);
          }
        }
        // may be generated resource
        if (result == null) {
          result = m_hostModeSupport.getGeneratedResource(publicResourcePath);
        }
        // may be no resource
        if (result == null) {
          return null;
        }
        // if CSS resource, then include "apply wait" class
        result = m_cssSupport.getResource(publicResourcePath, result);
        // done
        //System.out.println("-----------: " + result.length);
        return result;
      } catch (Throwable e) {
        throw ReflectionUtils.propagate(e);
      }
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // IHostedModeSupportFactory
  //
  ////////////////////////////////////////////////////////////////////////////
  private static final String HOSTED_MODE_SUPPORT_FACTORIES_POINT =
      "com.google.gdt.eclipse.designer.hosted.hostedModeFactory";

  private static List<IHostedModeSupportFactory> getHostedModeSupportFactories() {
    return ExternalFactoriesHelper.getElementsInstances(
        IHostedModeSupportFactory.class,
        HOSTED_MODE_SUPPORT_FACTORIES_POINT,
        "factory");
  }

  private IHostedModeSupport getHostedModeSupport() throws Exception {
    String versionString = m_version.getStringMajorMinor();
    // prepare factories
    List<IHostedModeSupportFactory> supportFactories = getHostedModeSupportFactories();
    if (supportFactories.isEmpty()) {
      throw new DesignerException(IExceptionConstants.NO_GWT_SDK_SUPPORT);
    }
    // ask each factory
    for (IHostedModeSupportFactory supportFactory : supportFactories) {
      IHostedModeSupport hostedModeSupport =
          supportFactory.create(versionString, m_parentClassLoader, m_moduleDescription);
      if (hostedModeSupport != null) {
        return hostedModeSupport;
      }
    }
    throw new DesignerException(IExceptionConstants.UNSUPPORTED_GWT_SDK, versionString);
  }
}
TOP

Related Classes of com.google.gdt.eclipse.designer.model.widgets.support.GwtState$ResourceProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.