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-2007 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.uml.core.roundtripframework.codegeneration;
043:
044: import java.util.Collection;
045: import java.util.Hashtable;
046: import java.util.List;
047:
048: import org.dom4j.Node;
049:
050: import org.netbeans.modules.uml.common.generics.ETPairT;
051: import org.netbeans.modules.uml.core.coreapplication.ICoreProduct;
052: import org.netbeans.modules.uml.core.coreapplication.IPreferenceManager2;
053: import org.netbeans.modules.uml.core.eventframework.EventBlocker;
054: import org.netbeans.modules.uml.core.metamodel.core.foundation.IElement;
055: import org.netbeans.modules.uml.core.metamodel.core.foundation.INamedElement;
056: import org.netbeans.modules.uml.core.metamodel.core.foundation.UMLXMLManip;
057: import org.netbeans.modules.uml.core.metamodel.infrastructure.coreinfrastructure.IClassifier;
058: import org.netbeans.modules.uml.core.metamodel.infrastructure.coreinfrastructure.INavigableEnd;
059: import org.netbeans.modules.uml.core.metamodel.structure.IProject;
060: import org.netbeans.modules.uml.core.preferenceframework.IPreferenceAccessor;
061: import org.netbeans.modules.uml.core.preferenceframework.PreferenceAccessor;
062: import org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.ILanguage;
063: import org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.ILanguageManager;
064: import org.netbeans.modules.uml.core.roundtripframework.IRoundTripController;
065: import org.netbeans.modules.uml.core.roundtripframework.RTMode;
066: import org.netbeans.modules.uml.core.support.umlmessagingcore.UMLMessagingHelper;
067: import org.netbeans.modules.uml.core.support.umlsupport.StringUtilities;
068: import org.netbeans.modules.uml.core.support.umlutils.ETList;
069: import org.netbeans.modules.uml.core.support.umlutils.NameManager;
070: import org.netbeans.modules.uml.ui.support.ProductHelper;
071: import org.netbeans.modules.uml.ui.support.ProgressDialogMessageKind;
072: import org.netbeans.modules.uml.ui.support.messaging.IModalModeKind;
073: import org.netbeans.modules.uml.ui.support.messaging.IProgressController;
074: import org.netbeans.modules.uml.ui.support.messaging.IProgressDialog;
075: import org.openide.util.NbPreferences;
076:
077: public class CodeGenerator implements ICodeGenerator,
078: IProgressController {
079: private String m_LanguageName = "";
080: private IProgressDialog m_ProgressDialog = null;
081: private boolean m_Cancelled = false;
082: private boolean m_Done = false;
083:
084: /**
085: *
086: * Generates code for the given language for all the elements passed in.
087: *
088: * @param languageName[in] Name of the language to generate in
089: * @param elements[in] The elements to generate for
090: *
091: * @return HRESULT
092: *
093: */
094: public void generateCode(String languageName,
095: ETList<IElement> pElements) {
096: if (pElements != null) {
097: ICoreProduct pProd = ProductHelper.getCoreProduct();
098: if (pProd != null) {
099: ILanguageManager pMan = pProd.getLanguageManager();
100: if (pMan != null) {
101: ILanguage pLang = pMan.getLanguage(languageName);
102: if (pLang != null) {
103: m_LanguageName = languageName;
104: generateCode(pLang, pElements);
105: } else {
106: if (languageName != null
107: && languageName.length() > 0) {
108: //UMLMessagingHelper help = new UMLMessagingHelper();
109: /*
110: UMLMessagingHelper __help( AfxGetResourceHandle(), IDS_MESSAGINGFACILITY );
111: CComBSTR message;
112: if( message.LoadString( AfxGetResourceHandle(), IDS_LANGUAGE_NOT_FOUND ))
113: {
114: USES_CONVERSION;
115: xstring mess( W2T( message ));
116:
117: _VH( __help.SendInfoMessage( mess.replace( mess.find( _T( "%1" )), 2, W2T( languageName ))));
118: }
119: */
120: }
121: }
122: }
123: }
124: }
125: }
126:
127: /**
128: *
129: * Loops through all the passed in elements, generating code for any
130: * element that does not have an artifact that is already associated with
131: * the passed in language
132: *
133: * @param pLang[in] The language that code is being generated in
134: * @param elements[in] The collection of elements to generate for
135: *
136: * @return HRESULT
137: *
138: */
139: private void generateCode(ILanguage pLang,
140: ETList<IElement> pElements) {
141: if (pLang != null && pElements != null) {
142: int cnt = pElements.size();
143: if (cnt > 0) {
144: establishProgress(cnt);
145: String tempMessage = translateString("IDS_GENERATING");
146: Integer i = new Integer(cnt);
147: String numFileBuffer = i.toString();
148: String message = tempMessage;
149:
150: String langName = pLang.getName();
151: ProjectKeeper keep = new ProjectKeeper();
152: RoundTripState state = new RoundTripState();
153: String unnamedValue = getUnnamedValue();
154:
155: boolean rtLive = state.m_Controller.getMode() == RTMode.RTM_LIVE;
156:
157: for (int x = 0; x < cnt && !m_Cancelled; x++) {
158: String groupMessage = message;
159: Integer i2 = new Integer(x + 1);
160: String buffer = i2.toString();
161: groupMessage = StringUtilities.replaceSubString(
162: groupMessage, "%1", buffer);
163: groupMessage = StringUtilities.replaceSubString(
164: groupMessage, "%2", numFileBuffer);
165: if (m_ProgressDialog != null) {
166: m_ProgressDialog.setGroupingTitle(groupMessage);
167: }
168: IElement pElement = pElements.get(x);
169: if (pElement instanceof INamedElement) {
170: INamedElement namedElement = (INamedElement) pElement;
171: String elementName = namedElement.getName();
172: if (m_ProgressDialog != null) {
173: m_ProgressDialog.setFieldOne(elementName);
174: }
175: // Make sure the element doesn't already have an
176: // artifact associated with the passed in language
177: if (oKToCodeGen(langName, namedElement)) {
178: if (!rtLive)
179: state.m_Controller
180: .setMode(RTMode.RTM_LIVE);
181:
182: generateCodeForElement(unnamedValue,
183: namedElement);
184:
185: if (!rtLive)
186: state.m_Controller
187: .setMode(RTMode.RTM_OFF);
188: }
189: } else {
190: if (m_ProgressDialog != null) {
191: String codeExists = translateString("IDS_CODE_EXISTS");
192: m_ProgressDialog
193: .setFieldTwo(
194: codeExists,
195: ProgressDialogMessageKind.PDMK_ERROR);
196: }
197: }
198: }
199:
200: if (m_ProgressDialog != null) {
201: m_ProgressDialog.increment();
202: m_ProgressDialog.clearFields();
203: }
204: keep.dispose();
205: state.dispose();
206: }
207: endProgress(false);
208: }
209: }
210:
211: private class ProjectKeeper {
212: private Hashtable<String, ProjectState> m_Projects = new Hashtable<String, ProjectState>();
213:
214: public ProjectKeeper() {
215: }
216:
217: public void dispose() {
218: Collection coll = m_Projects.values();
219: Object[] objs = coll.toArray();
220: int cnt = objs.length;
221: for (int x = 0; x < cnt; x++) {
222: ProjectState state = (ProjectState) objs[x];
223: if (state != null) {
224: state.cleanUp();
225: }
226: }
227: }
228:
229: /**
230: *
231: * We need to capture the state of the Project before we begin code gen, so that
232: * we can set it back after.
233: *
234: * @param languageName[in] The language name
235: * @param element[in] The element to code gen
236: *
237: * @return HRESULT
238: *
239: */
240:
241: boolean prepareProject(String languageName, IElement pElement) {
242: boolean prepared = false;
243: if (pElement != null) {
244: IProject pProj = pElement.getProject();
245: if (pProj != null) {
246: String xmiID = pProj.getXMIID();
247: ProjectState state = m_Projects.get(xmiID);
248: if (state != null) {
249: prepared = true;
250: } else {
251: EventBlocker blocker;
252: ProjectState newstate = new ProjectState(pProj);
253: pProj.setDefaultLanguage(languageName);
254: pProj.setMode("PSK_IMPLEMENTATION");
255: m_Projects.put(xmiID, newstate);
256: prepared = true;
257: }
258: }
259: }
260: return prepared;
261: }
262:
263: }
264:
265: /**
266: *
267: * This is a bit goofy. The way we're doing codegen right now is to make sure the
268: * Project that the element is in is in a code genable state, meaning that RoundTrip
269: * must be able to run. So the mode of the Project will be set to Implementation, and the
270: * default language will be set to the language specified in the initial call
271: * to GenerateCode.
272: *
273: * We're taking advantage of the fact that RoundTrip does 100% code gen on elements when
274: * transitioning from the Unnamed state, to a named state. So we block events, get
275: * the original name of the element, set it to the unnamed value, un plug events, then set
276: * the name back to the original name. This will kick roundtrip in, language specific
277: * request processors fire, and so on.
278: *
279: * @param unnamedValue[in] The value of the unnamed classifier preference
280: * @param element[in] The element to code gen
281: *
282: * @return HRESULT
283: *
284: */
285: private void generateCodeForElement(String unnamedValue,
286: INamedElement element) {
287: if (element != null) {
288: String origName = element.getName();
289: {
290: // Plug events so that nothing triggers for the setting back to the unnamedValue
291: boolean rtState = EventBlocker.startBlocking();
292: try {
293: ensureUniqueRoleNames(element);
294: element.setName(unnamedValue);
295: } finally {
296: EventBlocker.stopBlocking(rtState);
297: }
298: }
299:
300: // Now we need to pull all the SourceFileArtifacts off the element, just temporarily,
301: // so that roundtrip see this element as brand new.
302:
303: ArtifactState state = new ArtifactState(element);
304: // This will cause the code generation to happen
305: element.setName(origName);
306: state.dispose();
307: }
308: }
309:
310: /**
311: *
312: * Get the unnamed value for new elements
313: *
314: * @return The value
315: *
316: */
317: private String getUnnamedValue() {
318: String unnamed = "";
319: IPreferenceAccessor pPref = PreferenceAccessor.instance();
320: if (pPref != null) {
321: unnamed = pPref.getDefaultElementName();
322: }
323: return unnamed;
324: }
325:
326: private class RoundTripState {
327: private IRoundTripController m_Controller = null;
328: private int m_OrigMode = -1;
329:
330: public RoundTripState() {
331: ICoreProduct pProd = ProductHelper.getCoreProduct();
332: if (pProd != null) {
333: m_Controller = pProd.getRoundTripController();
334: if (m_Controller != null) {
335: m_OrigMode = m_Controller.getMode();
336: m_Controller.setMode(RTMode.RTM_LIVE);
337: }
338: }
339: }
340:
341: public void dispose() {
342: if (m_Controller != null) {
343: m_Controller.setMode(m_OrigMode);
344: }
345: }
346: }
347:
348: private class ProjectState {
349: private IProject m_Project = null;
350: private String m_OrigLang = "";
351: private String m_OrigMode = "";
352:
353: public ProjectState(IProject pProject) {
354: m_Project = pProject;
355: if (m_Project != null) {
356: m_OrigLang = m_Project.getDefaultLanguage();
357: m_OrigMode = m_Project.getMode();
358: }
359: }
360:
361: /*
362: CCodeGenerator::ProjectState::ProjectState( const ProjectState& copy )
363: {
364: Copy( copy );
365: }
366: */
367: public void cleanUp() {
368: if (m_Project != null) {
369: EventBlocker blocker;
370: m_Project.setDefaultLanguage(m_OrigLang);
371: m_Project.setMode(m_OrigMode);
372: }
373: }
374:
375: public void dispose() {
376: // Can't put this code in the destructor, as the act of putting
377: // a ProjectState into a map will cause the destruction
378: if (m_Project != null) {
379: EventBlocker blocker;
380: m_Project.setDefaultLanguage(m_OrigLang);
381: m_Project.setMode(m_OrigMode);
382: }
383: }
384: /*
385: void CCodeGenerator::ProjectState::Copy( const ProjectState& copy )
386: {
387: m_Project = copy.m_Project;
388: m_OrigMode = copy.m_OrigMode;
389: m_OrigLang = copy.m_OrigLang;
390: }
391: */
392: /*
393: CCodeGenerator::ProjectState& CCodeGenerator::ProjectState::operator=( const ProjectState& rh )
394: {
395: if( this != &rh )
396: {
397: Copy( rh );
398: }
399:
400: return *this;
401: }
402: */
403: }
404:
405: /**
406: *
407: * Determines whether or not the passed in element has any SourceFileArtifacts
408: * that are associated with the language whose name is passed in.
409: *
410: * @param name[in] Name of the language
411: * @param element[in] The element to check
412: *
413: * @return true if it is alright to codegen for the element, else false
414: *
415: */
416: private boolean oKToCodeGen(String name, IElement pElement) {
417: boolean ok = false;
418:
419: if (pElement != null) {
420: ETList<IElement> arts = pElement.getSourceFiles2(name);
421: if (arts != null) {
422: int num = arts.size();
423: if (num == 0) {
424: ok = true;
425: }
426: } else {
427: ok = true;
428: }
429: }
430: return ok;
431: }
432:
433: /**
434: *
435: * Called if the user hits the Cancel button on the ProgressDialog
436: *
437: *
438: * @return HRESULT
439: *
440: */
441: public void onCancelled() {
442: m_Cancelled = true;
443: m_ProgressDialog = null;
444: }
445:
446: /**
447: *
448: * Prepares the Progress Dialog
449: *
450: * @return HRESULT
451: */
452: public void establishProgress(int numFiles) {
453:
454: if (m_ProgressDialog == null) {
455: m_ProgressDialog = ProductHelper.getProgressDialog();
456: }
457:
458: if (m_ProgressDialog != null) {
459: m_ProgressDialog.setCollapse(true);
460: // Make the limits twice that of the number of files
461: // to take into account the integration phase
462: m_ProgressDialog.setLimits(new ETPairT<Integer, Integer>(
463: new Integer(0), new Integer((int) numFiles)));
464: String title = translateString("IDS_CODE_GEN_TITLE");
465: m_ProgressDialog.setTitle(title);
466: m_ProgressDialog
467: .setProgressController((IProgressController) this );
468: boolean bStatus = m_ProgressDialog
469: .display(IModalModeKind.MMK_MODELESS);
470: }
471: }
472:
473: /**
474: * Prepare the progress dialog for closure
475: *
476: * @param err[in]
477: *
478: * @return HRESULT
479: *
480: */
481: public void endProgress(boolean err) {
482:
483: boolean status = false;
484:
485: if (m_ProgressDialog != null) {
486:
487: String doneStr = translateString("IDS_DONE");
488: String errStr = translateString("IDS_ERROR");
489: String buttonText = doneStr;
490: m_Done = true;
491:
492: if (err) {
493: buttonText = errStr;
494: m_Done = false;
495: }
496:
497: m_ProgressDialog.clearFields();
498: m_ProgressDialog.setPosition(0);
499: m_ProgressDialog.setCollapse(false);
500:
501: m_ProgressDialog.promptForClosure(buttonText, true);
502: }
503: }
504:
505: public void onProgressEnd() {
506: this .onCancelled();
507: }
508:
509: /**
510: *
511: * ArtifactState removes any existing SourceFileArtifact elements from the passed in
512: * element temporarily ( it puts them back in the destructor ). This is done to prevent
513: * roundtrip for ignoring the default language set on the project, which code gen has
514: * just put for the specific language being code gend. For example, if a Java source file
515: * artifact is already assocated with the element, and the user wanted to gen into a VB file,
516: * RT would ignore the fact that CodeGen has temporarily put the the DefalualtLanguage
517: * property to VB, and would use the JavaRequestProcessor instead.
518: *
519: * @param element[in] The element about to be gend
520: *
521: */
522: private class ArtifactState {
523: private INamedElement m_Element = null;
524: private List m_RemovedArtifacts = null;
525:
526: public ArtifactState(INamedElement pElement) {
527: m_Element = pElement;
528: if (m_Element != null) {
529: Node node = m_Element.getNode();
530: if (node != null) {
531: m_RemovedArtifacts = node
532: .selectNodes("./UML:Element.ownedElement/UML:SourceFileArtifact");
533: if (m_RemovedArtifacts != null) {
534: int num = m_RemovedArtifacts.size();
535: for (int x = 0; x < num; x++) {
536: Node artNode = (Node) m_RemovedArtifacts
537: .get(x);
538: if (artNode != null) {
539: Node parent = artNode.getParent();
540: if (parent != null) {
541: artNode.detach();
542: }
543: }
544: }
545: }
546: }
547: }
548: }
549:
550: /**
551: * See the constructor comment for details
552: */
553: public void dispose() {
554: if (m_Element != null && m_RemovedArtifacts != null) {
555: Node node = m_Element.getNode();
556: if (node != null) {
557: Node parent = node
558: .selectSingleNode("./UML:Element.ownedElement");
559: if (parent != null) {
560: int num = m_RemovedArtifacts.size();
561: for (int x = 0; x < num; x++) {
562: Node artNode = (Node) m_RemovedArtifacts
563: .get(x);
564: if (artNode != null) {
565: ((org.dom4j.Element) parent)
566: .add(artNode);
567: }
568: }
569: }
570: }
571: }
572: }
573: }
574:
575: /**
576: *
577: * Makes sure that all navigable ends are properly named
578: *
579: * @param element[in] The element to query for navigable ends for
580: *
581: * @return HRESULT
582: *
583: */
584: private void ensureUniqueRoleNames(IElement element) {
585: if (element != null) {
586: if (element instanceof IClassifier) {
587: IClassifier classifier = (IClassifier) element;
588: ETList<INavigableEnd> ends = classifier
589: .getOutboundNavigableEnds();
590:
591: if (ends != null) {
592: int num = ends.size();
593: if (num > 0) {
594: NameManager manager = new NameManager();
595: for (int x = 0; x < num; x++) {
596: INavigableEnd end = ends.get(x);
597: if (end != null) {
598: String curName = end.getName();
599: if (curName == null
600: || curName.length() == 0) {
601: String attrPrefix = getAttrPrefix(end);
602: manager.ensureUniqueRoleName(end,
603: attrPrefix, attrPrefix, 0);
604: }
605: }
606: }
607: }
608: }
609: }
610: }
611: }
612:
613: /**
614: *
615: * Retrieves the name to use for a attribute or navigable end that includes
616: * the prefix set in the preferences and the name of the participant on the end.
617: *
618: * @param end[in] The end to generate a name for
619: *
620: * @return HRESULT
621: *
622: */
623: private String getAttrPrefix(INavigableEnd end) {
624: String prefix = "";
625: if (end != null) {
626: IClassifier pClass = end.getParticipant();
627: if (pClass != null) {
628: // Use this class as the type, which will be part of the default name.
629: prefix = getAttrPrefixFromPreferences();
630: String typeName = pClass.getName();
631: String attrName = prefix;
632: attrName += typeName;
633: prefix = attrName;
634: }
635: }
636: return prefix;
637: }
638:
639: /**
640: *
641: * Retrieves the language specific prefix to be used for attribute names
642: *
643: * @return The prefix, if any
644: *
645: */
646: private String getAttrPrefixFromPreferences() {
647: return NbPreferences.forModule(CodeGenerator.class).get(
648: "UML_ATTRIBUTE_PREFIX", "m"); // NOI18N
649: }
650:
651: private String getLanguageName() {
652: return m_LanguageName;
653: }
654:
655: private String translateString(String inStr) {
656: return DefaultCodeGenerationResource.getString(inStr);
657: }
658:
659: }
|