/* * Copyright 2011 cruxframework.org. * * 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 org.cruxframework.crux.core.rebind.screen; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.cruxframework.crux.core.declarativeui.screen.ScreenLoader; import org.cruxframework.crux.core.rebind.context.RebindContext; import org.cruxframework.crux.core.utils.StreamUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import com.google.gwt.core.ext.linker.EmittedArtifact.Visibility; import com.google.gwt.dev.resource.Resource; /** * Creates a representation for Crux screens * * @author Thiago Bustamante * */ public class ScreenFactory { private Map<String, Screen> screenCache = new HashMap<String, Screen>(); private ScreenLoader screenLoader; private ViewFactory viewFactory; private RebindContext context; /** * Singleton Constructor */ public ScreenFactory(RebindContext context) { this.context = context; this.screenLoader = context.getScreenLoader(); this.viewFactory = new ViewFactory(context); } /** * Factory method for screens. * @param id * @param device device property for this permutation being compiled * @return * @throws ScreenConfigException */ public Screen getScreen(String id, String device) throws ScreenConfigException { try { String cacheKey = device==null?id:id+"_"+device; Screen screen = screenCache.get(cacheKey); if (screen != null) { return screen; } Resource resource = screenLoader.getScreen(id); if (resource == null) { throw new ScreenConfigException("Error retrieving screen ["+id+"].crux.xml."); } InputStream inputStream = resource.openContents(); Document screenView = viewFactory.getViewDocument(id, device, inputStream); StreamUtils.safeCloseStream(inputStream); if (screenView == null) { throw new ScreenConfigException("Screen ["+id+"].crux.xml not found!"); } screen = parseScreen(id, device, screenView, resource.getLastModified()); if(screen != null) { screen.setLastModified(resource.getLastModified()); screenCache.put(cacheKey, screen); } return screen; } catch (IOException e) { throw new ScreenConfigException("Error retrieving screen ["+id+"].crux.xml.", e); } } public Set<String> getScreens() { return screenLoader.getScreens(); } /** * Retrieve the viewFactory. * @return */ public ViewFactory getViewFactory() { return viewFactory; } public void generateHostPages(String device) throws ScreenConfigException { Set<String> screenIDs = getScreens(); for (String screenId : screenIDs) { try { OutputStream stream = context.getGeneratorContext().tryCreateResource(context.getLogger(), screenId+".crux.xml"); if (stream != null) // was not already generated during this compilation { Resource resource = screenLoader.getScreen(screenId); InputStream inputStream = resource.openContents(); Document screenView = viewFactory.getViewDocument(screenId, device, inputStream); StreamUtils.safeCloseStream(inputStream); viewFactory.generateHTML(screenId, device, screenView, stream); StreamUtils.safeCloseStream(stream); context.getGeneratorContext().commitResource(context.getLogger(), stream).setVisibility(Visibility.Private); } } catch (Exception e) { throw new ScreenConfigException("Error generating HTML file for crux page ["+screenId+".crux.xml]", e); } } } /** * Parse the HTML page and build the Crux Screen. * @param id * @param device * @param screenView * @return * @throws IOException * @throws ScreenConfigException */ private Screen parseScreen(String id, String device, Document screenView, long lastModified) throws IOException, ScreenConfigException { Screen screen = null; String screenModule = getScreenModule(screenView); if(screenModule != null) { View rootView = viewFactory.getView(id, device, screenView, lastModified, true); screen = new Screen(id, screenModule, rootView); } else { throw new ScreenConfigException("No module declared on screen ["+id+"].crux.xml."); } return screen; } /** * @param nodeList * @return * @throws ScreenConfigException */ public static String getScreenModule(Document source) throws ScreenConfigException { String result = null; NodeList nodeList = source.getElementsByTagName("script"); int length = nodeList.getLength(); for (int i = 0; i < length; i++) { Element item = (Element) nodeList.item(i); String src = item.getAttribute("src"); if (src != null && src.endsWith(".nocache.js")) { if (result != null) { throw new ScreenConfigException("Multiple modules in the same html page is not allowed in CRUX."); } int lastSlash = src.lastIndexOf("/"); if(lastSlash >= 0) { int firstDotAfterSlash = src.indexOf(".", lastSlash); result = src.substring(lastSlash + 1, firstDotAfterSlash); } else { int firstDot = src.indexOf("."); result = src.substring(0, firstDot); } } } return result; } }