/* * � Copyright IBM Corp. 2006, 2014 * * 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. */ /* * Author: Maire Kehoe (mkehoe@ie.ibm.com) * Date: 19-Jan-2006 */ package com.ibm.xsp.test.framework.translator; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.ibm.commons.util.StringUtil; import com.ibm.xsp.page.compiled.PageToClassNameUtil; import com.ibm.xsp.page.parse.ComponentElement; import com.ibm.xsp.page.parse.FacesDeserializer; import com.ibm.xsp.page.parse.FacesReader; import com.ibm.xsp.page.translator.LogicalPage; import com.ibm.xsp.page.translator.PhysicalPage; import com.ibm.xsp.page.translator.TranslateErrorHandler; import com.ibm.xsp.page.translator.Translator; import com.ibm.xsp.registry.FacesSharableRegistry; import com.ibm.xsp.test.framework.AbstractXspTest; import com.ibm.xsp.test.framework.TestProject; import com.ibm.xsp.test.framework.XspTestFileUtil; import com.ibm.xsp.test.framework.XspTestUtil; import com.ibm.xsp.test.framework.setup.SkipFileContent; /** * Note, this is using private classes that are not part of the public APIs * and that may change in later releases, so the implementation of this class * may change over time, and the classes used here may not necessarily be reused * in your own code. For the public APIs, see * http://www-10.lotus.com/ldd/ddwiki.nsf/dx/XPages_Extensibility_API_Documentation * * @author Maire Kehoe (mkehoe@ie.ibm.com) * 19-Jan-2006 * Plugin: xsp.core.test * */ public class GeneratePagesTest extends AbstractXspTest{ private static final int TRANSLATOR_VERSION = Translator.TRANSLATOR_VERSION; private FacesSharableRegistry _tagRegistry; private DatestampList _datestampList; private String[] _pageNames; private String[] _skipDirsFromSubclass; @Override public String getDescription() { return "regenerates the compiled pages used in other tests"; } public class TranslatedAndSavedSignal extends RuntimeException{ private static final long serialVersionUID = 1L; public TranslatedAndSavedSignal(){} } @Override protected void setUp() throws Exception { super.setUp(); AbstractXspTest test = this; _pageNames = TestProject.getAllViewIds(test, TestProject.getUserDir(test)); _tagRegistry = TestProject.createRegistry(test); _datestampList = DatestampList.readLastChangeDates(TRANSLATOR_VERSION); _skipDirsFromSubclass = getSkipDirs(); } public void testPagesNotChanged() throws Exception { // the index to load, where -1 => load all of them // (change this value to debug an individual file) int pageToLoad = getDebugIndex(-1); //all // if should print to System.out boolean debugging = isDebugging(false); String fails = ""; // note, if translatePages fails it will fail this test List<PageInfo> pages = generatePageInfos(pageToLoad); if(pages.isEmpty()){ fail("No pages found to translate."); } // list of String pageName to the old Date of the .xsp file if( _datestampList.isTranslatorVersionChanged() ){ if( debugging ){ System.out.println(GeneratePagesTest.class.getName() + ".testPagesNotChanged() : " + "Translator changed. Will attempt to regenerate all pages."); } } Map<String, Object> projectObjs = new HashMap<String, Object>(); File userDir = TestProject.getUserDir(this); String absoluteDir = userDir.getAbsolutePath(); FacesSharableRegistry registry = getRegistry(); List<String> customPages = TestProject.getCustomControlPageNames(registry); for (PageInfo pageInfo : pages) { int fileIndex = pageInfo.index; String name = pageInfo.pageName; long xspFileDate = _datestampList.getCurrentDateLong(pageInfo.getXspFile()); File javaFile = getJavaFile(pageInfo); if( pageToLoad == -1 && javaFile.exists() ){ // check if the .xsp file's date has changed if( !_datestampList.isFileDatestampChanged(name, xspFileDate) ){ if( debugging ){ logNoChangeInPage(pageInfo, fileIndex); } continue; } } if( debugging ){ logGeneratingPage(pageInfo, fileIndex, _datestampList.isTranslatorVersionChanged()); } // set the new date into the map _datestampList.updateTimestamp(name, xspFileDate); // translate the page String page; try{ page = translatePage(registry, customPages, projectObjs, pageInfo, debugging && 1 == pages.size()); pageInfo.setTranslated(page); }catch(TranslatedAndSavedSignal signal){ // one of the subclasses saves the file itself. String failMsg = "* " + name+" File generated, refresh the project."; System.err.println("[" + fileIndex + "] " + failMsg); fails += failMsg +"\n"; continue; }catch(Exception ex){ String msg = ex.getMessage(); if( null == msg ){ msg = ex.toString(); } String failMsg = "# " + pageInfo.pageName + " Problem translating file: "+msg; System.err.println("[" + fileIndex + "] " + failMsg); fails += failMsg+"\n"; continue; } if( ! fileContentsChanged(javaFile, page) ){ continue; } // output the changed file. boolean success = writeToFile(javaFile, page); if( ! success ){ String failMsg = "# " + javaFile.getAbsolutePath().substring( absoluteDir.length()) + " Problem writing to file (Hijack?)"; System.err.println("[" + fileIndex + "] " + failMsg); fails += failMsg+"\n"; }else{ String failMsg = "* " + name+ " File generated, refresh the project."; System.err.println("[" + fileIndex + "] " + failMsg); fails += failMsg+"\n"; } } if( _datestampList.isSomeDatestampChangedSinceRead() ){ _datestampList.writeLastChangeDates( userDir, getGeneratedSourceFolder(), TRANSLATOR_VERSION); } fails = XspTestUtil.removeMultilineFailSkips(fails, SkipFileContent.concatSkips(getSkippedFails(), this, "testPagesNotChanged")); if ( fails.length() > 0) { fail(XspTestUtil.getMultilineFailMessage(fails)); } } /** * @return */ protected String[] getSkippedFails() { return StringUtil.EMPTY_STRING_ARRAY; } private void logNoChangeInPage(PageInfo pageInfo, int fileIndex) { System.out.println(GeneratePagesTest.class .getName() + ".testPageCompile() : " + "No change in [" + fileIndex + "] " + pageInfo.pageName); } private void logGeneratingPage(PageInfo pageInfo, int fileIndex, boolean translatorVersionChanged) { if( translatorVersionChanged ){ System.out.println(GeneratePagesTest.class .getName() + ".testPageCompile() : " + "Attempting to regenerate [" + fileIndex + "] " + pageInfo.pageName); }else{ System.out.println(GeneratePagesTest.class .getName() + ".testPageCompile() : " + "Timestamp change in [" + fileIndex + "] " + pageInfo.pageName); } } /** * Available to override in subclasses * @param defaultValue * @return */ protected int getDebugIndex(int defaultValue) { return defaultValue; } /** * Available to override in subclasses */ protected boolean isDebugging(boolean defaultVal) { return defaultVal; } /** * Available to be called in subclasses. * @return */ protected DatestampList getDatestampList(){ return _datestampList; } /** * Available to be called in subclasses. * @return */ protected String[] getPageNames(){ return _pageNames; } /** * Available to be called in subclasses. * @return */ protected File toXspFile(String pageName){ return new File(TestProject.getUserDir(this),pageName); } public static boolean writeToFile(File file, String page) { try { file.getParentFile().mkdirs(); file.createNewFile(); if( ! file.canWrite() ){ return false; } FileWriter out = new FileWriter(file); out.write(page); out.close(); return true; //success } catch (IOException e) { e.printStackTrace(); fail("Couldn't write to the file : "+file.getAbsolutePath()); } return false; } private boolean fileContentsChanged(File javaFilePath, String pageContents) { if( ! javaFilePath.exists() ){ // changed return true; } if( javaFilePath.isDirectory() ){ throw new IllegalArgumentException( "The java file path is a directory: " + javaFilePath.getAbsolutePath()); } String existing = XspTestFileUtil.readFileContents(javaFilePath); return ! pageContents.equals( existing ); } private File getJavaFile(PageInfo pageInfo) { File javaFile = pageInfo.getJavaFile(); if( null != javaFile ){ return javaFile; } String fileName = getGeneratedSourceFolder() + "/"+pageInfo.getClassName().replaceAll("\\.","/")+".java"; javaFile = new File(TestProject.getUserDir(this), fileName); pageInfo.setJavaFile(javaFile); return javaFile; } protected String getGeneratedSourceFolder() { return "gen"; } protected List<PageInfo> generatePageInfos(int pageToLoad){ String[] pageNames = getPageNames(); int numPages = pageNames.length; List<PageInfo> translated = new ArrayList<PageInfo>(numPages); for (int i = 0; i < numPages; i++) { if( pageToLoad >= 0 && pageToLoad != i ){ continue; } String pageName = pageNames[i]; // if the file is in one of the directories to skip. if( isSkipped( pageName ) ){ // skip this file continue; } File xspFile = toXspFile(pageName); String fileName = xspFile.getAbsolutePath(); PageInfo pageInfo = new PageInfo(i, pageName, fileName); pageInfo.setXspFile(xspFile); pageInfo.setEnsureMarkupTags(isEnsureMarkupTags(pageName)); translated.add(pageInfo); } return translated; } /** * Available to be overridden in the subclass, may throw a TranslatedAndSavedSignal. * @param registry * @param customPages * @param projectObjs * @param pageInfo * @param debugging * if <code>true</code> the page will be printed to System.out * @return the string containing the translated page .java * @throws Exception */ protected String translatePage(FacesSharableRegistry registry, List<String> customPages, Map<String, Object> projectObjs, PageInfo pageInfo, boolean debugging) throws Exception { String result = translatePage(registry, projectObjs, customPages, getApplicationVersion(), pageInfo, getTranslateHandler()); if( debugging ){ System.out.println(GeneratePagesTest.class.getName() + ".testPageCompile() : \n"+result); } return result; } public static String translatePage(FacesSharableRegistry registry, Map<String, Object> projectObjs, List<String> customsPageNames, String applicationVersion, PageInfo pageInfo, TranslateErrorHandler errHandler) throws Exception { // read in the persistence tree. File xspFile = pageInfo.getXspFile(); FacesDeserializer deserial; boolean ensureMarkupTags = pageInfo.isEnsureMarkupTags(); String key = ensureMarkupTags? "ensureMarkupTags" : "plainDeserial"; if( null != projectObjs && projectObjs.containsKey(key) ){ deserial = (FacesDeserializer) projectObjs.get(key); }else{ Map<String, Object> options = new HashMap<String, Object>(); // allowNamespacedMarkupTags defaults to true in FacesDeserializer // but defaults to false in the design-time code. options.put(FacesDeserializer.OPTION_ALLOW_NAMESPACED_MARKUP_TAGS, ensureMarkupTags); deserial = new FacesDeserializer(registry, options); if( null != projectObjs ){ projectObjs.put(key, deserial); } } InputStream in = new BufferedInputStream(new FileInputStream(xspFile)); ComponentElement root; try{ FacesReader reader = new FacesReader(in); root = deserial.readRoot(reader); }finally{ in.close(); } assertNotNull("Unable to deserialize the file : " + xspFile, root); Map<String, Object> options = new HashMap<String, Object>(); options.put(Translator.OPTION_APPLICATION_VERSION, applicationVersion); options.put(Translator.OPTION_ERROR_HANDLER, errHandler); if( null != projectObjs ){ // array copied from ControlClassUtil.initCache(Translator, Map<String, Object>) String[] keys = {"text","tag","custom-base","include-page","property-map-instance"}; for (String transKey : keys) { Class<?> value = (Class<?>) projectObjs.get(transKey); if( value != null ){ options.put(transKey, value); } } } Translator compiler = new Translator(registry, options); boolean isCustomControl = customsPageNames.contains(pageInfo.pageName); LogicalPage logical = new LogicalPage(pageInfo.getClassName(), pageInfo.pageName, isCustomControl); // note, not handling pages composed of multiple .xsp files, so just assume a single physical page: PhysicalPage physical = new PhysicalPage("", root,"", 0); logical.addMainPage(physical); // generate the .java class String result = compiler.translate(logical); return result; } /** * Available to override in subclasses; */ protected TranslateErrorHandler getTranslateHandler() { return null; } /** * Available to override in subclasses; */ protected String getApplicationVersion(){ return null; } /** * Available to be called in subclass implementations of {@link #generatePageInfos(int)} * Should not be overridden is subclasses - override {@link #getSkipDirs()} instead. * @param pageName * @return */ protected boolean isSkipped(String pageName){ return -1 != XspTestUtil.startsWithIndex(_skipDirsFromSubclass, pageName); } /** * Available to override in subclasses * @return */ protected String[] getSkipDirs() { return StringUtil.EMPTY_STRING_ARRAY; } /** * Available to be called in subclass implementations of {@link #generatePageInfos(int)}, * and available to be overridden when the .xsp files use unknown HTML markup namespaces * see SPR#MKEE89JPHG - this behavior differs from the Designer .java file generation, * as in Designer 8.5.3 the change in behavior has not been implemented, * so it is as if this method always returns true. * @param xspFileName * @return */ protected boolean isEnsureMarkupTags(String xspFileName){ return false; } protected FacesSharableRegistry getRegistry() { return _tagRegistry; } @Override protected String[][] getExtraConfig() { return XspTestUtil.concat(super.getExtraConfig(), new String[][]{ // load the xsp-config files in this project {"target.local.xspconfigs","true"}, }); } public static class PageInfo{ public final int index; public final String pageName; public final String xspFileName; private boolean ensureMarkupTags; private String translated; private String className; private File xspFile; private File javaFile; private Object detail; /** * @return the javaFile */ public File getJavaFile() { return javaFile; } /** * @param javaFile the javaFile to set */ public void setJavaFile(File javaFile) { this.javaFile = javaFile; } public PageInfo(final int index, final String pageName, final String xspFileName) { this.index = index; this.pageName = pageName; this.xspFileName = xspFileName; } public File getXspFile(){ if( null == xspFile ){ xspFile = new File(xspFileName); } return xspFile; } public void setXspFile(File xspFile){ this.xspFile = xspFile; } public boolean isEnsureMarkupTags() { return ensureMarkupTags; } public void setEnsureMarkupTags(boolean ensureMarkupTags) { this.ensureMarkupTags = ensureMarkupTags; } public String getTranslated() { return translated; } public void setTranslated(String translated) { this.translated = translated; } public String getClassName() { if( null == className ){ className = PageToClassNameUtil.getClassNameForPage(pageName); } return className; } public Object getDetail() { return detail; } public void setDetail(Object detail) { this.detail = detail; } @Override public String toString(){ return new StringBuffer("[").append(index).append(']').append( pageName).toString(); } } }