Java tutorial
/* * Copyright (C) 2010-11 Mobile Developer Solutions * * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php * * 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. */ /* * References: * org.com.android.ide.eclipse.adt.internal.wizards.newproject * Platform Plug-in Developer Guide > Programmer's Guide > Dialogs and wizards */ package com.mds.apg.wizards; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.net.URL; import java.util.Enumeration; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.osgi.framework.Bundle; class PhonegapProjectPopulate { /** * Creates the actual project(s). This is run asynchronously in a different * thread. * * @param monitor An existing monitor. * @param mainData Data for main project. Can be null. * @throws InvocationTargetException to wrap any unmanaged exception and * return it to the calling thread. The method can fail if it * fails to create or modify the project or if it is canceled by * the user. */ static void createProjectAsync(IProgressMonitor monitor, PageInfo pageInfo) throws InvocationTargetException { monitor.beginTask("Create Android Project", 100); try { updateProjectWithPhonegap(monitor, pageInfo); } catch (CoreException e) { throw new InvocationTargetException(e); } catch (IOException e) { throw new InvocationTargetException(e); } catch (URISyntaxException e) { throw new InvocationTargetException(e); } finally { monitor.done(); } } /** * Driver for the various tasks to add phonegap. * 1. Update the main java file to load index.html * 2. Get the phonegap.jar and update the classpath * 3. Get the user's sources into the android project * 4. Handle project add-ins like Sencha and JQuery Mobile * 5. Update the AndroidManifest file * 6. Fill the res directory with drawables and layout * 7. Update the project nature so that JavaScript files are recognize * 8. Refresh the project with the updated disc files * 9. Do a clean build - TODO is clean build still necessary with ADT 8.0.1? * * @param monitor An existing monitor. * @throws InvocationTargetException to wrap any unmanaged exception and * return it to the calling thread. The method can fail if it * fails to create or modify the project or if it is canceled by * the user. */ static private void updateProjectWithPhonegap(IProgressMonitor monitor, PageInfo pageInfo) throws CoreException, IOException, URISyntaxException { updateJavaMain(pageInfo); getPhonegapJar(monitor, pageInfo); getWWWSources(monitor, pageInfo); if (pageInfo.mJqmChecked) setupJqm(monitor, pageInfo); if (pageInfo.mSenchaChecked) setupSencha(monitor, pageInfo); phonegapizeAndroidManifest(pageInfo); getResFiles(monitor, pageInfo); IProject newAndroidProject = pageInfo.mAndroidProject; addJsNature(monitor, newAndroidProject); newAndroidProject.refreshLocal(2 /* DEPTH_INFINITE */, monitor); newAndroidProject.build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); } /** * Find and update the main java file to kick off phonegap * * @throws IOException */ static private void updateJavaMain(PageInfo pageInfo) throws IOException { String destDir = pageInfo.mDestinationDirectory; String srcDir = destDir + "src"; String javaFile = findJavaFile(srcDir); String javaFileContents; if (javaFile == null) { // ADT 20 - Need to create Java file from scratch String dir = srcDir; String packageName = ""; do { File f = new File(dir); if (!f.isDirectory()) { throw new IOException("Unexpected android src directory structure"); } String fList[] = f.list(); if (fList.length > 0) { packageName = packageName.equals("") ? fList[0] : packageName + '.' + fList[0]; dir = dir + '/' + fList[0]; } else { break; } } while (true); javaFileContents = "package " + packageName + ";\n\n"; javaFileContents += "import " + (pageInfo.mIsCordova ? "org.apache.cordova.DroidGap;\n" : "import com.phonegap.*;\n"); javaFileContents += "import android.os.Bundle;\n\n"; javaFileContents += "public class MyPhoneGapActivity extends DroidGap {\n"; javaFileContents += "\t@Override\n"; javaFileContents += "\tpublic void onCreate(Bundle savedInstanceState) {\n"; javaFileContents += "\t\tsuper.onCreate(savedInstanceState);\n"; javaFileContents += "\t\tsuper.loadUrl(\"file:///android_asset/www/index.html\");\n"; javaFileContents += "\t}\n"; javaFileContents += "}\n"; javaFile = dir + "/" + "MyPhoneGapActivity.java"; } else { // pre-ADT 20 javaFileContents = StringIO.read(javaFile); // Import com.phonegap instead of Activity if (pageInfo.mIsCordova) { javaFileContents = javaFileContents.replace("import android.app.Activity;", "import org.apache.cordova.DroidGap;"); } else { javaFileContents = javaFileContents.replace("import android.app.Activity;", "import com.phonegap.*;"); } // Change superclass to DroidGap instead of Activity javaFileContents = javaFileContents.replace("extends Activity", "extends DroidGap"); // Change to start with index.html javaFileContents = javaFileContents.replace("setContentView(R.layout.main);", "super.loadUrl(\"file:///android_asset/www/index.html\");"); } // Write out the file StringIO.write(javaFile, javaFileContents); } // Recursively search for java file. Assuming there is only one in the new // Android project static private String findJavaFile(String dir) { String retVal; File f = new File(dir); if (f.isDirectory()) { String fList[] = f.list(); for (String s : fList) { if (s.length() > 5 && s.indexOf(".java") == s.length() - 5) { return dir + '/' + s; } else { retVal = findJavaFile(dir + '/' + s); if (retVal != null) return retVal; } } } return null; } /** * If using github install, phonegap.jar does not yet exist in a raw phonegap * installation It needs to be built with the Android installation. So * instead, we'll get the sources, so that it just gets build with our * product. We also need to reference /framework/libs/commons-codec-1.3.jar upon * which the sources depend * * For a non-github install, just point at phonegap.jar * * @throws URISyntaxException */ static private void getPhonegapJar(IProgressMonitor monitor, PageInfo pageInfo) throws CoreException, IOException, URISyntaxException { addDefaultDirectories(pageInfo.mAndroidProject, "", new String[] { "libs" }, monitor); String libsDir = pageInfo.mDestinationDirectory + "libs/"; if (pageInfo.mPackagedPhonegap) { addDefaultDirectories(pageInfo.mAndroidProject, "", new String[] { "libs" }, monitor); String v = pageInfo.mPhonegapVersion; String phonegapJarFileName = (pageInfo.mIsCordova ? "cordova-" : "phonegap-") + v + ".jar"; InputStream stream = bundleGetFileAsStream("/resources/phonegap/" + v + "/" + phonegapJarFileName); FileCopy.coreStreamCopy(stream, new File(libsDir + phonegapJarFileName)); updateClasspath(monitor, pageInfo.mAndroidProject, libsDir + phonegapJarFileName, null); } else if (pageInfo.mFromGitHub) { String toDir = pageInfo.mDestinationDirectory + "/src"; FileCopy.recursiveCopy(pageInfo.mPhonegapDirectory + "/framework/src", toDir); String destJar = libsDir + "commons-codec-1.3.jar"; FileCopy.copy(pageInfo.mPhonegapDirectory + "/framework/libs/commons-codec-1.3.jar", destJar); updateClasspath(monitor, pageInfo.mAndroidProject, destJar, new Path(toDir)); } else { // not from github String destJar = libsDir + pageInfo.mPhonegapJar; FileCopy.copy(pageInfo.mPhonegapDirectory + pageInfo.mInstallAndroidDirectory + pageInfo.mPhonegapJar, destJar); updateClasspath(monitor, pageInfo.mAndroidProject, destJar, null); } } /** * Update the classpath with thanks to Larry Isaacs in * http://dev.eclipse.org/newslists/news.eclipse.webtools/msg10002.html * * With ADT r17 classpath is no longer needed. The Android tools include anything in libs * * @throws CoreException * * @throws URISyntaxException */ static private void updateClasspath(IProgressMonitor monitor, IProject androidProject, String jarFile, Path srcLoc) throws CoreException { IJavaProject javaProject = (IJavaProject) androidProject.getNature(JavaCore.NATURE_ID); IClasspathEntry[] classpathList = javaProject.readRawClasspath(); IClasspathEntry[] newClasspaths = new IClasspathEntry[classpathList.length + 1]; System.arraycopy(classpathList, 0, newClasspaths, 0, classpathList.length); // Create the new Classpath entry IClasspathEntry newPath = JavaCore.newLibraryEntry(new Path(jarFile), srcLoc, null); newClasspaths[classpathList.length] = newPath; // write it back out javaProject.setRawClasspath(newClasspaths, monitor); } /** * Get the sources from the example directory or alternative specified * directory Place them in assets/www Also get phonegap.js from framework * assets * * @throws URISyntaxException */ static private void getWWWSources(IProgressMonitor monitor, PageInfo pageInfo) throws CoreException, IOException, URISyntaxException { addDefaultDirectories(pageInfo.mAndroidProject, "assets/", new String[] { "www" }, monitor); String wwwDir = pageInfo.mDestinationDirectory + "/assets/www/"; String contentSelection = pageInfo.mContentSelection; boolean useExample = contentSelection.equals("example"); boolean doCopy = true; if (pageInfo.mUseJqmDemo) { bundleCopy("/resources/jqm/demo2", wwwDir); doCopy = false; } else if (pageInfo.mJqmChecked) { if (useExample) { bundleCopy("/resources/jqm/phonegapExample", wwwDir); doCopy = false; } } else if (pageInfo.mSenchaChecked) { if (useExample && !pageInfo.mSenchaKitchenSink) { bundleCopy("/resources/sencha/phonegapExample", wwwDir); doCopy = false; } } if (doCopy) { if (pageInfo.mPackagedPhonegap && useExample && !pageInfo.mSenchaKitchenSink) { bundleCopy("resources/phonegap/Sample", wwwDir); } else if (contentSelection.equals("minimal")) { bundleCopy("/resources/phonegap/minimal", wwwDir); } else { FileCopy.recursiveCopy(pageInfo.mSourceDirectory, wwwDir); } } if (pageInfo.mPureImport) return; // Don't tweak anything String phonegapJsFileName; if (pageInfo.mPackagedPhonegap) { String v = pageInfo.mPhonegapVersion; phonegapJsFileName = (pageInfo.mIsCordova ? "cordova-" : "phonegap-") + v + ".js"; InputStream stream = bundleGetFileAsStream("/resources/phonegap/" + v + "/" + phonegapJsFileName); FileCopy.coreStreamCopy(stream, new File(wwwDir + phonegapJsFileName)); } else if (pageInfo.mFromGitHub) { // Even though there is a phonegap.js file in the directory // framework/assets/www, it is WRONG!! // phonegap.js must be constructed from the files in // framework/assets/js FileCopy.createPhonegapJs(pageInfo.mPhonegapDirectory + "/framework/" + "assets/js", wwwDir + "phonegap.js"); phonegapJsFileName = "phonegap.js"; } else { // www.phonegap.com/download phonegapJsFileName = pageInfo.mPhonegapJs; if (!contentSelection.equals("user") && !pageInfo.mSenchaKitchenSink) { // copy phonegap{version}.js to phonegap.js if (contentSelection.equals("minimal") || pageInfo.mJqmChecked || pageInfo.mSenchaChecked) { // otherwise already there FileCopy.copy( pageInfo.mPhonegapDirectory + pageInfo.mInstallAndroidDirectory + pageInfo.mPhonegapJs, wwwDir + pageInfo.mPhonegapJs); } } else { // otherwise keep the name, since the user controls the // index.html (and don't overwrite if user supplied the phonegap.js) FileCopy.copyDontOverwrite( pageInfo.mPhonegapDirectory + pageInfo.mInstallAndroidDirectory + pageInfo.mPhonegapJs, wwwDir + pageInfo.mPhonegapJs); } } // Make sure index.html has the right phonegap.js String indexHtmlContents = StringIO.read(wwwDir + "index.html"); indexHtmlContents = indexHtmlContents.replaceFirst("src=\"(cordova|phonegap)[a-zA-Z-.0-9]*js\"", "src=\"" + phonegapJsFileName + "\""); if (indexHtmlContents.indexOf("src=\"phonegap") < 0 && indexHtmlContents.indexOf("src=\"cordova") < 0) { // no phonegap*.js in file int index = indexHtmlContents.lastIndexOf("</head>"); if (index > 0) { index = indexHtmlContents.lastIndexOf("</script>", index); if (index > 0) { index += 9; indexHtmlContents = indexHtmlContents.substring(0, index) + "\n\t<!-- Uncomment following line to access PhoneGap APIs (not necessary to use PhoneGap to package web app) -->\n" + "\t<!-- <script type=\"text/javascript\" charset=\"utf-8\" src=\"" + phonegapJsFileName + "\"></script>-->\n" + indexHtmlContents.substring(index); } } } StringIO.write(wwwDir + "index.html", indexHtmlContents); if (pageInfo.mSenchaKitchenSink) { // delete the confusing index_android.html try { File f = new File(wwwDir + "index_android.html"); f.delete(); } catch (Exception e) { // Ignore any exceptions here } } } /** * Set up contents if jQuery Mobile is selected * * @throws URISyntaxException */ static private void setupJqm(IProgressMonitor monitor, PageInfo pageInfo) throws CoreException, IOException, URISyntaxException { addDefaultDirectories(pageInfo.mAndroidProject, "assets/www/", new String[] { "jquery.mobile" }, monitor); String jqmDir = pageInfo.mDestinationDirectory + "assets/www/jquery.mobile/"; String fromJqmDir = pageInfo.mJqmDirectory; String version; if (fromJqmDir == null) { // get from plugin installation version = "-1.1.0"; // TODO - do this programmatically bundleCopy("/resources/jqm/jquery.mobile", jqmDir); } else { version = pageInfo.mJqmVersion; String fileName = "/jquery.mobile" + version + ".js"; FileCopy.copy(fromJqmDir + fileName, jqmDir + fileName); fileName = "/jquery.mobile" + version + ".css"; FileCopy.copy(fromJqmDir + fileName, jqmDir + fileName); fileName = "/jquery.mobile" + version + ".min.js"; FileCopy.copy(fromJqmDir + fileName, jqmDir + fileName); fileName = "/jquery.mobile" + version + ".min.css"; FileCopy.copy(fromJqmDir + fileName, jqmDir + fileName); FileCopy.recursiveCopy(fromJqmDir + "/images", jqmDir + "/images"); } bundleCopy("/resources/jqm/supplements", jqmDir); if (!pageInfo.mPureImport && !pageInfo.mContentSelection.equals("minimal")) { // Update the index.html with path to the js and css files String file = pageInfo.mDestinationDirectory + "/" + "assets/www/index.html"; String fileContents = FileStringReplace.replace(file, "\\{\\$jqmversion\\}", version); fileContents = updatePathInHtml(fileContents, "jquery.mobile" + version, ".css\"", "\"jquery.mobile/", pageInfo.mSourceDirectory, null); fileContents = updatePathInHtml(fileContents, "jquery.mobile" + version, ".js\"", "\"jquery.mobile/", pageInfo.mSourceDirectory, null); // and jquery file fileContents = updatePathInHtml(fileContents, "jquery-1.7.2", ".js\"", "\"jquery.mobile/", pageInfo.mSourceDirectory, ".min\""); // Add CDN comments for jQuery Mobile fileContents = fileContents.replace("</head>", "\n\t<!-- CDN Respositories: For production, replace lines above with these uncommented minified versions -->\n" + "\t<!-- <link rel=\"stylesheet\" href=\"http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css\" />-->\n" + "\t<!-- <script src=\"http://code.jquery.com/jquery-1.7.2.min.js\"></script>-->\n" + "\t<!-- <script src=\"http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js\"></script>-->\n\t</head>"); // Write out the file StringIO.write(file, fileContents); } } /** * Get sencha-touch.js and resources directory. Add references to them in * index.html If kitchen sink is selected, so other copies TBD * * @throws URISyntaxException */ static private void setupSencha(IProgressMonitor monitor, PageInfo pageInfo) throws CoreException, IOException, URISyntaxException { addDefaultDirectories(pageInfo.mAndroidProject, "assets/www/", new String[] { "sencha" }, monitor); addDefaultDirectories(pageInfo.mAndroidProject, "assets/www/sencha/", new String[] { "resources" }, monitor); String senchaDir = pageInfo.mDestinationDirectory + "/" + "assets/www/sencha/"; // The .scss files confuse JSDT on Linux FileCopy.recursiveCopySkipSuffix(pageInfo.mSenchaDirectory + "/resources", senchaDir + "resources", ".scss"); // Now copy the sencha-touch*.js FileCopy.copy(pageInfo.mSenchaDirectory + "/sencha-touch.js", senchaDir); FileCopy.copy(pageInfo.mSenchaDirectory + "/sencha-touch-debug.js", senchaDir); if (!pageInfo.mPureImport && !pageInfo.mContentSelection.equals("minimal")) { // Update the index.html with path to sencha-touch.css and sencha-touch.js String file = pageInfo.mDestinationDirectory + "/" + "assets/www/index.html"; String fileContents = StringIO.read(file); fileContents = updatePathInHtml(fileContents, "sencha-touch", ".css\"", "\"sencha/resources/css/", pageInfo.mSourceDirectory, null); fileContents = updatePathInHtml(fileContents, "sencha-touch", ".js\"", "\"sencha/", pageInfo.mSourceDirectory, null); // Write out the file StringIO.write(file, fileContents); } } /** * Get the Android Manifest file and tweak it for phonegap * * @throws URISyntaxException */ static private void phonegapizeAndroidManifest(PageInfo pageInfo) throws CoreException, IOException, URISyntaxException { String destFile = pageInfo.mDestinationDirectory + "AndroidManifest.xml"; String sourceFileContents; if (pageInfo.mPackagedPhonegap) { sourceFileContents = bundleGetFileAsString("/resources/phonegap/AndroidManifest.xml"); } else { String sourceFile; if (pageInfo.mFromGitHub) { sourceFile = pageInfo.mPhonegapDirectory + "/framework/AndroidManifest.xml"; } else { sourceFile = pageInfo.mPhonegapDirectory + pageInfo.mInstallExampleDirectory + "AndroidManifest.xml"; } sourceFileContents = StringIO.read(sourceFile); } String manifestInsert = getManifestScreensAndPermissions(sourceFileContents); String destFileContents = StringIO.read(destFile); // Add phonegap screens, permissions destFileContents = destFileContents.replaceFirst("<application\\s+android:", manifestInsert + "<application" + " android:"); if (destFileContents.indexOf("<activity") < 0) { // This is ADT 20 String newActivity = " <activity\n"; newActivity += " android:name=\".MyPhoneGapActivity\"\n"; newActivity += " android:label=\"@string/app_name\" >\n"; newActivity += " <intent-filter>\n"; newActivity += " <action android:name=\"android.intent.action.MAIN\" />\n"; newActivity += " <category android:name=\"android.intent.category.LAUNCHER\" />\n"; newActivity += " </intent-filter>\n"; newActivity += " </activity>\n"; destFileContents = destFileContents.replace("</application>", newActivity + " </application>"); // Add android:configChanges="orientation|keyboardHidden" to the activity destFileContents = destFileContents.replaceFirst("<activity\\s+android:", "<activity android:configChanges=\"orientation|keyboardHidden\"\n android:"); } else { // pre ADT 20 // Add android:configChanges="orientation|keyboardHidden" to the activity destFileContents = destFileContents.replaceFirst("<activity\\s+android:", "<activity android:configChanges=\"orientation|keyboardHidden\" android:"); // Copy additional activities from source to destination - especially the DroidGap activity int activityIndex = sourceFileContents.indexOf("<activity"); int secondActivityIndex = sourceFileContents.indexOf("<activity", activityIndex + 1); if (secondActivityIndex > 0) { int endIndex = sourceFileContents.lastIndexOf("</activity>"); destFileContents = destFileContents.replace("</activity>", "</activity>\n\t\t" + sourceFileContents.substring(secondActivityIndex, endIndex + 11)); } if (destFileContents.indexOf("<uses-sdk") < 0) { // User did not set min SDK, so use the phonegap template manifest version int startIndex = sourceFileContents.indexOf("<uses-sdk"); int endIndex = sourceFileContents.indexOf("<", startIndex + 1); destFileContents = destFileContents.replace("</manifest>", sourceFileContents.substring(startIndex, endIndex) + "</manifest>"); } } // Write out the file StringIO.write(destFile, destFileContents); } /** * Helper Function for phonegapizeAndroidManifest It finds the big middle * section that needs to be added to the manifest for phonegap */ static private String getManifestScreensAndPermissions(String manifest) { int startIndex; startIndex = manifest.indexOf("<supports-screens"); if (startIndex == -1) startIndex = manifest.indexOf("<uses-permissions"); if (startIndex == -1) return null; int index = startIndex; int lastIndex; do { lastIndex = index; index = manifest.indexOf("<uses-permission", lastIndex + 1); if (index < 0) { // <uses-feature added in PhoneGap 1.0.0 manifest index = manifest.indexOf("<uses-feature", lastIndex + 1); } } while (index > 0); lastIndex = manifest.indexOf('<', lastIndex + 1); return manifest.substring(startIndex, lastIndex); } /** * Copy anything in res/layout Copy drawable to drawable* Leave values alone * since string maps to app name * * @throws URISyntaxException */ static private void getResFiles(IProgressMonitor monitor, PageInfo pageInfo) throws CoreException, IOException, URISyntaxException { String destResDir = pageInfo.mDestinationDirectory + "res" + "/"; if (pageInfo.mPackagedPhonegap) { bundleCopy("/resources/phonegap/layout", pageInfo.mDestinationDirectory + "/res/layout/"); bundleCopy("/resources/phonegap/" + pageInfo.mPhonegapVersion + "/res", pageInfo.mDestinationDirectory + "/res/"); // xml directory // Copy resource drawable to all of the project drawable* directories File destDir = new File(destResDir); String fList[] = destDir.list(); for (String s : fList) { if (s.indexOf("drawable") == 0) { File drawableDir = new File(destResDir + s); String fList2[] = drawableDir.list(); for (String f : fList2) { if (f.endsWith(".png")) { // SDK Tools 14 moved default image from icon.png to ic_launcher.png, so this code is now more generic InputStream sourceDrawable = bundleGetFileAsStream( "/resources/phonegap/icons/mdspgicon.png"); FileCopy.coreStreamCopy(sourceDrawable, new File(destResDir + s + "/" + f)); } } } } return; } String sourceResDir; if (pageInfo.mFromGitHub) { sourceResDir = pageInfo.mPhonegapDirectory + "/framework/res/"; } else { sourceResDir = pageInfo.mPhonegapDirectory + pageInfo.mInstallExampleDirectory + "res/"; } FileCopy.recursiveForceCopy(sourceResDir + "layout/", destResDir + "layout/"); if (FileCopy.exists(sourceResDir + "xml/")) { FileCopy.recursiveCopy(sourceResDir + "xml/", destResDir + "xml/"); } if (pageInfo.mFromGitHub) { // Copy source drawable to all of the project drawable* directories String sourceDrawableDir = sourceResDir + "drawable" + "/"; File destFile = new File(destResDir); String fList[] = destFile.list(); for (String s : fList) { if (s.indexOf("drawable") == 0) { FileCopy.recursiveForceCopy(sourceDrawableDir, destResDir + s); } } } else { // the drawables are already in the final directories FileCopy.recursiveForceCopy(sourceResDir + "drawable-hdpi", destResDir + "drawable-hdpi"); FileCopy.recursiveForceCopy(sourceResDir + "drawable-ldpi", destResDir + "drawable-ldpi"); FileCopy.recursiveForceCopy(sourceResDir + "drawable-mdpi", destResDir + "drawable-mdpi"); } } /** * Adds default directories to the project. Unchanged from private version * in parent class * * @param project The Java Project to update. * @param parentFolder The path of the parent folder. Must end with a * separator. * @param folders Folders to be added. * @param monitor An existing monitor. * @throws CoreException if the method fails to create the directories in * the project. */ static private void addDefaultDirectories(IProject project, String parentFolder, String[] folders, IProgressMonitor monitor) throws CoreException { for (String name : folders) { if (name.length() > 0) { IFolder folder = project.getFolder(parentFolder + name); if (!folder.exists()) { folder.create(true /* force */, true /* local */, new SubProgressMonitor(monitor, 10)); } } } } /** * Add JavaScript nature to the project. It gets added last after Android * and Java ones. * * @throws CoreException */ static private void addJsNature(IProgressMonitor monitor, IProject project) throws CoreException { final String JS_NATURE = "org.eclipse.wst.jsdt.core.jsNature"; if (!project.hasNature(JS_NATURE)) { IProjectDescription description = project.getDescription(); String[] natures = description.getNatureIds(); String[] newNatures = new String[natures.length + 1]; System.arraycopy(natures, 0, newNatures, 0, natures.length); newNatures[natures.length] = JS_NATURE; description.setNatureIds(newNatures); project.setDescription(description, new SubProgressMonitor(monitor, 10)); } } static private String updatePathInHtml(String fileContents, String fileName, String suffix, String prepend, String indexHtmlDirectory, String suffixOverride) throws IOException { String fullName = fileName + ".min" + suffix; int fileNameIndex = fileContents.indexOf(fullName); if (fileNameIndex <= 0) { fullName = fileName + ".min"; // No .js ok for min to get around eclipse issues with min files fileNameIndex = fileContents.indexOf(fullName); } if (fileNameIndex <= 0) { fullName = fileName + "-debug" + suffix; fileNameIndex = fileContents.indexOf(fullName); } if (fileNameIndex <= 0) { fullName = fileName + suffix; fileNameIndex = fileContents.indexOf(fullName); } if (fileNameIndex > 0) { // Found it int startIncludeIndex = fileContents.lastIndexOf("\"", fileNameIndex); fileContents = fileContents.substring(0, startIncludeIndex) + prepend + fileContents.substring(fileNameIndex); } else { // must add a new line. Bug if indexOf finds stuff inside comments int insertSpot = fileContents.indexOf("</head>"); int firstIndex = fileContents.indexOf(suffix); if (firstIndex > 0) { insertSpot = fileContents.lastIndexOf('<', firstIndex); } else if (suffix == ".css\"") { // no css includes in source int firstJsIndex = fileContents.indexOf(".js\""); if (firstJsIndex > 0) { insertSpot = fileContents.lastIndexOf('<', firstJsIndex); } } if (insertSpot <= 0) { throw new IOException( "Supplied index.html in " + indexHtmlDirectory + " is missing the </head> tag"); } // adjust insertSpot back to end of last line while (Character.isWhitespace(fileContents.charAt(--insertSpot))) ; insertSpot++; if (suffix.equals(".js\"")) { fileContents = fileContents.substring(0, insertSpot) + "\n <script type=\"text/javascript\" src=" + prepend + (suffixOverride != null ? (fileName + suffixOverride) : fullName) + "></script>" + fileContents.substring(insertSpot); } else if (suffix.equals(".css\"")) { fileContents = fileContents.substring(0, insertSpot) + "\n <link rel=\"stylesheet\" href=" + prepend + fullName + " type=\"text/css\">" + fileContents.substring(insertSpot); } else { throw new IllegalArgumentException("updatePathInHtml called with unsupported suffix"); } } return fileContents; } static private void bundleCopy(String dir, String destination) throws IOException, URISyntaxException { Bundle bundle = com.mds.apg.Activator.getDefault().getBundle(); @SuppressWarnings("unchecked") Enumeration<URL> en = bundle.findEntries(dir, "*", true); while (en.hasMoreElements()) { URL url = en.nextElement(); String pathFromBase = url.getPath().substring(dir.length() + 1); String toFileName = destination + pathFromBase; if (toFileName.indexOf("/.svn/") >= 0) continue; File toFile = new File(toFileName); if (pathFromBase.lastIndexOf('/') == pathFromBase.length() - 1) { // This is a directory - create it if (!toFile.mkdir()) { throw new IOException("bundleCopy: " + "directory Creation Failed: " + toFileName); } } else { // This is a file - copy it FileCopy.coreStreamCopy(url.openStream(), toFile); } } } static private InputStream bundleGetFileAsStream(String fileName) throws IOException, URISyntaxException { Bundle bundle = com.mds.apg.Activator.getDefault().getBundle(); URL url = bundle.getEntry(fileName); return url.openStream(); } static private String bundleGetFileAsString(String fileName) throws IOException, URISyntaxException { return StringIO.convertStreamToString(bundleGetFileAsStream(fileName)); } }