001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.projectimport.eclipse;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import java.net.URL;
047: import java.util.ArrayList;
048: import java.util.Collection;
049: import java.util.HashMap;
050: import java.util.HashSet;
051: import java.util.Iterator;
052: import java.util.Map;
053: import java.util.Set;
054: import java.util.logging.Level;
055: import java.util.logging.Logger;
056: import org.netbeans.api.java.platform.JavaPlatform;
057: import org.netbeans.api.java.platform.JavaPlatformManager;
058: import org.netbeans.api.java.project.JavaProjectConstants;
059: import org.netbeans.api.project.ProjectManager;
060: import org.netbeans.api.project.ant.AntArtifact;
061: import org.netbeans.api.project.ant.AntArtifactQuery;
062: import org.netbeans.modules.java.api.common.SourceRoots;
063: import org.netbeans.modules.java.j2seplatform.platformdefinition.PlatformConvertor;
064: import org.netbeans.modules.java.j2seplatform.wizard.NewJ2SEPlatform;
065: import org.netbeans.modules.java.j2seproject.J2SEProject;
066: import org.netbeans.modules.java.j2seproject.J2SEProjectGenerator;
067: import org.netbeans.modules.java.j2seproject.J2SEProjectType;
068: import org.netbeans.modules.java.j2seproject.ui.customizer.J2SEProjectProperties;
069: import org.netbeans.modules.projectimport.LoggerFactory;
070: import org.netbeans.spi.java.project.classpath.ProjectClassPathExtender;
071: import org.netbeans.spi.project.support.ant.AntProjectHelper;
072: import org.netbeans.spi.project.support.ant.EditableProperties;
073: import org.netbeans.spi.project.support.ant.PropertyUtils;
074: import org.openide.ErrorManager;
075: import org.openide.filesystems.FileObject;
076: import org.openide.filesystems.FileUtil;
077: import org.openide.filesystems.Repository;
078: import org.openide.loaders.DataFolder;
079: import org.openide.loaders.DataObject;
080: import org.openide.util.NbBundle;
081: import org.openide.util.RequestProcessor;
082: import org.w3c.dom.Element;
083:
084: /**
085: * Able to import given Eclipse projects in separate thread with providing
086: * information about current state(progress). Converts eclipse projects and
087: * their required projects into NetBeans ones and stores them into the given
088: * destination.
089: *
090: * @author mkrauskopf
091: */
092: final class Importer {
093:
094: /** Logger for this class. */
095: private static final Logger logger = LoggerFactory.getDefault()
096: .createLogger(Importer.class);
097:
098: private final Set/*<EclipseProject>*/eclProjects;
099: private final String destination;
100: private final boolean recursively;
101: private J2SEProject[] nbProjects;
102: private final Set/*<String>*/recursionCheck;
103: private final Map/*<String, J2SEProject>*/loadedProject;
104:
105: private int nOfProcessed;
106: private String progressInfo;
107: private boolean done;
108: private Collection warnings;
109:
110: private JavaPlatform[] nbPlfs; // All netbeans platforms
111: private File nbDefPlfFile; // NetBeans default platform directory
112:
113: Importer(final Set/*<EclipseProject>*/eclProjects,
114: String destination, boolean recursively) {
115: this .eclProjects = eclProjects;
116: this .destination = destination;
117: this .recursively = recursively;
118: this .nbProjects = new J2SEProject[eclProjects.size()];
119: recursionCheck = new HashSet();
120: loadedProject = new HashMap();
121: }
122:
123: /**
124: * Starts importing process in separated thread. Use getters to obtain
125: * information about current progress.
126: */
127: void startImporting() {
128: nbPlfs = JavaPlatformManager.getDefault()
129: .getInstalledPlatforms();
130: JavaPlatform defPlf = JavaPlatformManager.getDefault()
131: .getDefaultPlatform();
132: Collection installFolder = defPlf.getInstallFolders();
133: if (installFolder.isEmpty()) {
134: logWarning(NbBundle.getMessage(Importer.class,
135: "MSG_NotValidPlatformsInNB")); // NOI18N
136: return;
137: } else {
138: nbDefPlfFile = FileUtil.toFile((FileObject) installFolder
139: .toArray()[0]);
140: }
141: RequestProcessor.getDefault().post(new Runnable() {
142: public void run() {
143: ProjectManager.mutex().writeAccess(new Runnable() {
144: public void run() {
145: try {
146: int pos = 0;
147: for (Iterator it = eclProjects.iterator(); it
148: .hasNext();) {
149: EclipseProject eclPrj = (EclipseProject) it
150: .next();
151: nbProjects[pos++] = importProject(eclPrj);
152: }
153: } catch (Exception ex) {
154: logger
155: .log(
156: Level.WARNING,
157: "Error occurred during project importing",
158: ex); // NOI18N
159: } finally {
160: done = true;
161: }
162: }
163: });
164: }
165: });
166: }
167:
168: /**
169: * Returns number of already processed projects.
170: */
171: int getNOfProcessed() {
172: return nOfProcessed;
173: }
174:
175: /**
176: * Returns localized message describing current importer activity.
177: */
178: String getProgressInfo() {
179: return progressInfo;
180: }
181:
182: /**
183: * Returns whether importer has finished.
184: */
185: boolean isDone() {
186: return done;
187: }
188:
189: Collection getWarnings() {
190: return warnings;
191: }
192:
193: /**
194: * Gets imported projects. Call after the importing <code>isDone()</code>.
195: */
196: J2SEProject[] getProjects() {
197: return nbProjects;
198: }
199:
200: private J2SEProject importProject(EclipseProject eclProject)
201: throws IOException {
202: assert eclProject != null : "Eclipse project cannot be null"; // NOI18N
203:
204: // recursivity check
205: if (!recursionCheck.add(eclProject.getDirectory().toString())) {
206: J2SEProject project = (J2SEProject) loadedProject
207: .get(eclProject.getDirectory().getAbsolutePath());
208: return project;
209: }
210: logger.finer("Importing of project: \""
211: + // NOI18N
212: eclProject.getDirectory().getAbsolutePath()
213: + "\" started"); // NOI18N
214: nOfProcessed++;
215: progressInfo = NbBundle.getMessage(Importer.class,
216: "MSG_Progress_ProcessingProject", eclProject.getName()); // NOI18N
217: File nbProjectDir = FileUtil.normalizeFile(new File(destination
218: + "/" + eclProject.getName())); // NOI18N
219: Map eclRoots = eclProject.getAllSourceRoots();
220: File[] testDirs = new File[0];
221: File[] srcFiles = new File[eclRoots.size()];
222: int j = 0;
223: for (Iterator it = eclRoots.keySet().iterator(); it.hasNext();) {
224: srcFiles[j++] = (File) it.next();
225: }
226: // create basic NB project
227: final AntProjectHelper helper = J2SEProjectGenerator
228: .createProject(nbProjectDir, eclProject.getName(),
229: srcFiles, testDirs, null, null);
230: // get NB project
231: J2SEProject nbProject = (J2SEProject) ProjectManager
232: .getDefault().findProject(
233: FileUtil.toFileObject(FileUtil
234: .normalizeFile(nbProjectDir)));
235: ProjectClassPathExtender nbProjectClassPath = (ProjectClassPathExtender) nbProject
236: .getLookup().lookup(ProjectClassPathExtender.class);
237: assert nbProjectClassPath != null : "Cannot lookup ProjectClassPathExtender"; // NOI18N
238:
239: // set labels for source roots
240: SourceRoots roots = nbProject.getSourceRoots();
241: URL[] rootURLs = roots.getRootURLs();
242: String[] labels = new String[rootURLs.length];
243: for (int i = 0; i < rootURLs.length; i++) {
244: labels[i] = (String) eclRoots.get(new File(rootURLs[i]
245: .getFile()));
246: }
247: roots.putRoots(rootURLs, labels);
248:
249: // add libraries to classpath
250: for (Iterator it = eclProject.getAllLibrariesFiles().iterator(); it
251: .hasNext();) {
252: File eclLib = (File) it.next();
253: if (eclLib.exists()) {
254: nbProjectClassPath.addArchiveFile(FileUtil
255: .toFileObject(FileUtil.normalizeFile(eclLib)));
256: } else {
257: logWarning(
258: NbBundle.getMessage(Importer.class,
259: "MSG_LibraryDoesnExist", // NOI18N
260: eclProject.getName(), eclLib
261: .getAbsolutePath()), true);
262: }
263: }
264:
265: // create projects the main project depends on
266: if (recursively) {
267: Collection projects = eclProject.getProjects();
268: for (Iterator it = projects.iterator(); it.hasNext();) {
269: EclipseProject eclSubProject = (EclipseProject) it
270: .next();
271: J2SEProject nbSubProject = importProject(eclSubProject);
272: // The project can be null when a cycle dependency is encountered.
273: // Just skip the dependency and try the best we can.
274: if (nbSubProject != null) {
275: AntArtifact[] artifact = AntArtifactQuery
276: .findArtifactsByType(
277: nbSubProject,
278: JavaProjectConstants.ARTIFACT_TYPE_JAR);
279: nbProjectClassPath.addAntArtifact(artifact[0],
280: artifact[0].getArtifactLocations()[0]);
281: } else {
282: logger
283: .warning("Project in directory \""
284: + // NOI18N
285: eclProject.getDirectory()
286: .getAbsolutePath()
287: + "\" is already being processed. Recursive "
288: + // NOI18N
289: "dependencies reached. "); // NOI18N
290: }
291: }
292: }
293:
294: // set platform used by an Eclipse project
295: setJavaPlatform(eclProject, helper);
296:
297: ProjectManager.getDefault().saveProject(nbProject);
298: logger.finer("Project loaded: " + // NOI18N
299: eclProject.getDirectory().getAbsolutePath());
300: loadedProject.put(eclProject.getDirectory().getAbsolutePath(),
301: nbProject);
302: return nbProject;
303: }
304:
305: /** Sets <code>JavaPlatform</code> for the given project */
306: private void setJavaPlatform(EclipseProject eclProject,
307: final AntProjectHelper helper) throws IOException {
308: // progressInfo = "Setting JDK for \"" + eclProject.getName() + "\"";
309: String eclPlfDir = eclProject.getJDKDirectory();
310: // eclPlfDir can be null in a case when a JDK was set for an eclipse
311: // project in Eclipse then the directory with JDK was deleted from
312: // filesystem and then a project is imported into NetBeans
313: if (eclPlfDir == null) {
314: return;
315: }
316: File eclPlfFile = FileUtil.normalizeFile(new File(eclPlfDir));
317: if (eclPlfFile.equals(nbDefPlfFile)) { // use default platform
318: return;
319: }
320: JavaPlatform nbPlf = null;
321: for (int i = 0; i < nbPlfs.length; i++) {
322: JavaPlatform current = nbPlfs[i];
323: Collection instFolders = current.getInstallFolders();
324: if (instFolders.isEmpty()) {
325: logger.fine("Java platform \""
326: + current.getDisplayName() + // NOI18N
327: "\" is not valid. Skipping..."); // NOI18N
328: continue;
329: }
330: File nbPlfDir = FileUtil.toFile((FileObject) instFolders
331: .toArray()[0]);
332: if (nbPlfDir.equals(eclPlfFile)) {
333: nbPlf = nbPlfs[i];
334: // found
335: break;
336: }
337: }
338: // If we are not able to find any platform let's use the "broken
339: // platform" which can be easily added by user with "Resolve Reference
340: // Problems" feature. Such behaviour is much better then using a default
341: // platform when user imports more projects.
342: if (nbPlf == null) {
343: logger.fine("Creating new platform: "
344: + eclPlfFile.getAbsolutePath()); // NOI18N
345: FileObject fo = FileUtil.toFileObject(eclPlfFile);
346: if (fo != null) {
347: NewJ2SEPlatform plat = NewJ2SEPlatform.create(fo);
348: plat.run();
349: if (plat.isValid()) {
350: if (plat.findTool("javac") != null) { //NOI18N
351: String displayName = createPlatformDisplayName(plat);
352: String antName = createPlatformAntName(displayName);
353: plat.setDisplayName(displayName);
354: plat.setAntName(antName);
355: FileObject platformsFolder = Repository
356: .getDefault()
357: .getDefaultFileSystem()
358: .findResource(
359: "Services/Platforms/org-netbeans-api-java-Platform"); //NOI18N
360: assert platformsFolder != null;
361: DataObject dobj = PlatformConvertor.create(
362: plat, DataFolder
363: .findFolder(platformsFolder),
364: antName);
365: nbPlf = (JavaPlatform) dobj.getNodeDelegate()
366: .getLookup().lookup(JavaPlatform.class);
367: // update installed platform
368: nbPlfs = JavaPlatformManager.getDefault()
369: .getInstalledPlatforms();
370: } else {
371: logWarning(NbBundle.getMessage(Importer.class,
372: "MSG_JRECannotBeUsed", // NOI18N
373: eclProject.getName()), true);
374: }
375: } else {
376: // tzezula: TODO: User should be notified in the UI and
377: // probably default platform is used (not sure if it is
378: // according to UI spec)
379: logWarning("Cannot create new J2SE platform, the " + // NOI18N
380: "default platform will be used."); // NOI18N
381: }
382: } else {
383: logWarning(NbBundle.getMessage(Importer.class,
384: "MSG_JDKDoesnExistUseDefault", // NOI18N
385: eclProject.getName(), eclPlfFile
386: .getAbsolutePath()), true);
387: }
388: }
389: // tzezula: The platform is changed to explicit one only in case when
390: // the platform already existed or it was successfully created
391: if (nbPlf != null) {
392: Element pcd = helper.getPrimaryConfigurationData(true);
393: Element el = pcd.getOwnerDocument().createElementNS(
394: J2SEProjectType.PROJECT_CONFIGURATION_NAMESPACE,
395: "explicit-platform"); // NOI18N
396: pcd.appendChild(el);
397: helper.putPrimaryConfigurationData(pcd, true);
398: EditableProperties prop = helper
399: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
400: String ver = nbPlf.getSpecification().getVersion()
401: .toString();
402: String normalizedName = (String) nbPlf.getProperties().get(
403: "platform.ant.name"); // NOI18N
404: prop.setProperty(J2SEProjectProperties.JAVAC_SOURCE, ver);
405: prop.setProperty(J2SEProjectProperties.JAVAC_TARGET, ver);
406: prop.setProperty(J2SEProjectProperties.JAVA_PLATFORM,
407: normalizedName);
408: helper.putProperties(
409: AntProjectHelper.PROJECT_PROPERTIES_PATH, prop);
410: } else {
411: logWarning("Setting of platform for project \"" // NOI18N
412: + eclProject.getName() + "\" failed."); // NOI18N
413: }
414: }
415:
416: private void logWarning(String message) {
417: logWarning(message, false);
418: }
419:
420: /**
421: * Delegates to ErrorManager. When the <code>isGUIWarning</code> is true,
422: * the warning will be also shown to the user after importing is done.
423: */
424: private void logWarning(String message, boolean isGUIWarning) {
425: if (isGUIWarning) {
426: if (warnings == null) {
427: warnings = new ArrayList();
428: }
429: warnings.add(message);
430: }
431: logger.warning(message);
432: }
433:
434: private static String createPlatformDisplayName(JavaPlatform plat) {
435: Map m = plat.getSystemProperties();
436: String vmName = (String) m.get("java.vm.name"); //NOI18N
437: String vmVersion = (String) m.get("java.vm.version"); //NOI18N
438: StringBuffer displayName = new StringBuffer();
439: if (vmName != null) {
440: displayName.append(vmName);
441: }
442: if (vmVersion != null) {
443: if (displayName.length() > 0) {
444: displayName.append(' ');
445: }
446: displayName.append(vmVersion);
447: }
448: return displayName.toString();
449: }
450:
451: private String createPlatformAntName(String displayName) {
452: assert displayName != null && displayName.length() > 0;
453: String antName = PropertyUtils
454: .getUsablePropertyName(displayName);
455: if (platformExists(antName)) {
456: String baseName = antName;
457: int index = 1;
458: antName = baseName + Integer.toString(index);
459: while (platformExists(antName)) {
460: index++;
461: antName = baseName + Integer.toString(index);
462: }
463: }
464: return antName;
465: }
466:
467: /**
468: * Checks if the platform of given antName is already installed
469: */
470: private boolean platformExists(String antName) {
471: assert antName != null && antName.length() > 0;
472: for (int i = 0; i < nbPlfs.length; i++) {
473: String otherName = (String) nbPlfs[i].getProperties().get(
474: "platform.ant.name"); //NOI18N
475: if (antName.equals(otherName)) {
476: return true;
477: }
478: }
479: return false;
480: }
481:
482: }
|