Java tutorial
/******************************************************************************* * 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.Maps; import com.google.common.collect.Sets; import com.google.gdt.eclipse.designer.Activator; import com.google.gdt.eclipse.designer.util.DefaultModuleDescription; import com.google.gdt.eclipse.designer.util.ModuleDescription; import com.google.gdt.eclipse.designer.util.Utils; import org.eclipse.wb.internal.core.utils.IOUtils2; 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.execution.RunnableObjectEx; import org.eclipse.core.resources.IFile; import org.eclipse.jface.text.Document; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import java.text.MessageFormat; import java.util.List; import java.util.Map; import java.util.Set; /** * Support for CSS resources in {@link GwtState}. * * @author scheglov_ke * @author mitin_aa * @coverage gwt.model */ public final class CssSupport { private final GwtState state; private List<String> resources; private List<IFile> files; private long nextRequestId; private final Map<IFile, Long> filesStampMap = Maps.newHashMap(); private final Set<String> waitRequestSet = Sets.newHashSet(); private final Set<String> waitApplySet = Sets.newHashSet(); //////////////////////////////////////////////////////////////////////////// // // Constructor // //////////////////////////////////////////////////////////////////////////// public CssSupport(GwtState state) { this.state = state; } //////////////////////////////////////////////////////////////////////////// // // Access // //////////////////////////////////////////////////////////////////////////// /** * @return the CSS resources. */ public List<String> getResources() { return resources; } /** * @return the {@link IFile}s for CSS resources. */ public List<IFile> getFiles() { return files; } //////////////////////////////////////////////////////////////////////////// // // Private access // //////////////////////////////////////////////////////////////////////////// /** * Prepares CSS resources and files. */ void prepareResources() throws Exception { ModuleDescription moduleDescription = state.getModuleDescription(); resources = Utils.getCssResources(moduleDescription); if (moduleDescription instanceof DefaultModuleDescription) { files = Utils.getFilesForResources(((DefaultModuleDescription) moduleDescription).getFile(), resources); for (IFile file : files) { filesStampMap.put(file, file.getModificationStamp()); } } } /** * Adds links to the CSS resources. */ void addLinkDeclarations(List<String> declarations) { for (String cssResource : resources) { declarations.add( MessageFormat.format("<link rel=''stylesheet'' type=''text/css'' href=''{0}''/>", cssResource)); } } /** * Adds DIVs for CSS "apply wait", see {@link #waitFor()} JavaDoc. */ String addReloadingFeature(String html) { StringBuilder declarations = new StringBuilder(); for (String resource : resources) { String name = getWaitRequestName(resource); declarations.append("<div class='"); declarations.append(name); declarations.append("'></div>\n"); } return StringUtils.replace(html, "%CSS_WAIT_DECLARATIONS%", declarations.toString()); } /** * @return <code>true</code> if one or more CSS files were modified (and schedules them for * reloading with next refresh). */ boolean isModified() { waitRequestSet.clear(); waitApplySet.clear(); boolean modified = false; // check CSS files boolean hasModifiedCSSFiles = false; for (Map.Entry<IFile, Long> entry : filesStampMap.entrySet()) { IFile file = entry.getKey(); long storedStamp = entry.getValue(); long fileStamp = file.getModificationStamp(); if (fileStamp != storedStamp) { modified = true; filesStampMap.put(file, fileStamp); hasModifiedCSSFiles = true; } } // schedule CSS load waiting if (hasModifiedCSSFiles) { synchronized (waitRequestSet) { for (String resource : resources) { String waitRequestName = getWaitRequestName(resource); waitRequestSet.add(waitRequestName); } } } // if has modified CSS files, ask Browser for reload if (modified) { ExecutionUtils.runLog(new RunnableEx() { public void run() throws Exception { state.getHostModeSupport().invokeNativeVoid("__reload_css", ArrayUtils.EMPTY_CLASS_ARRAY, ArrayUtils.EMPTY_OBJECT_ARRAY); } }); waitFor(); } // return final modification state return modified; } /** * Provides wait operation for CSS files to be applied. * * CSS files waiting works as following: * <p> * When HTTP-server recognizes request for CSS resource it added into requested CSS-file fake CSS * class with style which applying caused browser to request some resource from HTTP-server, e.g. * HTTP-server adds something like that: * * <pre> * .gwt__wait_stylesheetXXXX { * visibility: hidden; * background: url("cssFileName_cssFileTimestamp"); * } * </pre> * Where: XXXX is some unique identifier (hashCode for CSS file name), cssFileName is the name of * requested CSS file cssFileTimestamp is the timestamp of requested CSS file. So, when the * browser applies received CSS file with fake CSS class it requests * "cssFileName_cssFileTimestamp" resource from HTTP-server. * <p> * Note: browser would not request "cssFileName_cssFileTimestamp" resource without any * HTML-element with fake CSS class applied, thats why its needed to dynamically generate the HTML * element with fake CSS class for every CSS file in project (see set CSS wait declarations in * constructor). */ void waitFor() { long startWait = System.currentTimeMillis(); while (true) { // may be done boolean done = true; synchronized (waitRequestSet) { done &= waitRequestSet.isEmpty(); } synchronized (waitApplySet) { done &= waitApplySet.isEmpty(); } if (done) { break; } // do not wait more than 500ms if (System.currentTimeMillis() - startWait > 500) { break; } // wait more state.runMessagesLoop(); } } /** * @return the name of CSS class to use for waiting given CSS file which is referred by * {@link IFile} or public resource path. */ private static String getWaitRequestName(String path) { String name = path; name = StringUtils.replace(name, "/", "_"); name = StringUtils.removeEndIgnoreCase(name, ".css"); return "wbp__wait_stylesheet_" + name; } //////////////////////////////////////////////////////////////////////////// // // Content access // //////////////////////////////////////////////////////////////////////////// /** * @return the empty PNG image bytes, if this resource path is "wait for CSS" marker, or * <code>null</code> if some other resource for requested. */ byte[] getResourceWait(String publicResourcePath) { synchronized (waitApplySet) { if (waitApplySet.remove(publicResourcePath)) { // IE requires the content, otherwise 'image.complete' is always false. // This doesn't affect other browsers though, but returning some content is the right way. return ExecutionUtils.runObjectIgnore(new RunnableObjectEx<byte[]>() { public byte[] runObject() throws Exception { return IOUtils2.readBytes(Activator.getFile("icons/empty.png")); } }, null); } } return null; } /** * @param result * the bytes of CSS file. * @return the updates bytes of CSS file, with "wait for loading" markers added. */ byte[] getResource(String publicResourcePath, byte[] result) throws Exception { if (publicResourcePath.toLowerCase().endsWith(".css")) { if (resources.contains(publicResourcePath)) { // prepare "wait apply" resource, should be image String waitRequestName = getWaitRequestName(publicResourcePath); String waitApplyName = waitRequestName + "_" + nextRequestId++ + ".png"; // prepare content String cssContent = new String(result, "UTF-8"); cssContent = addRulesMarkers(publicResourcePath, cssContent); // add "wait apply" class to the end of CSS content { cssContent += "\n." + waitRequestName + "{"; cssContent += "visibility: hidden; "; cssContent += "background-image: url('"; cssContent += state.m_moduleBaseURL + waitApplyName; cssContent += "'); }\n"; result = cssContent.getBytes("UTF-8"); } // update request/apply sets synchronized (waitApplySet) { waitApplySet.add(waitApplyName); } synchronized (waitRequestSet) { waitRequestSet.remove(waitRequestName); } } } return result; } private String addRulesMarkers(String publicResourcePath, String content) throws Exception { Document sourceDocument = new Document(content); /*CssEditContext editContext = new CssEditContext(sourceDocument); CssDocument document = editContext.getCssDocument(); for (CssRuleNode rule : document.getRules()) { int ruleId = m_lastTrackedRule++; rule.addDeclaration(CssFactory.newDeclaration("wbp-css-rule-" + ruleId, "0")); } System.out.println(sourceDocument.get());*/ return sourceDocument.get(); } }