~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to xml/refactoring/src/org/netbeans/modules/xml/refactoring/XMLRefactoringTransaction.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
 
 
42
/*
 
43
 * XMLRefactoringTransaction.java
 
44
 *
 
45
 * Created on February 1, 2007, 2:32 PM
 
46
 *
 
47
 * To change this template, choose Tools | Template Manager
 
48
 * and open the template in the editor.
 
49
 */
 
50
 
 
51
package org.netbeans.modules.xml.refactoring;
 
52
 
 
53
import java.io.IOException;
 
54
import java.net.URI;
 
55
import java.net.URISyntaxException;
 
56
import java.net.URL;
 
57
import java.util.ArrayList;
 
58
import java.util.Collection;
 
59
import java.util.HashMap;
 
60
import java.util.HashSet;
 
61
import java.util.List;
 
62
import java.util.Map;
 
63
import java.util.Set;
 
64
import java.util.logging.Level;
 
65
import java.util.logging.Logger;
 
66
import javax.swing.event.UndoableEditEvent;
 
67
import javax.swing.event.UndoableEditListener;
 
68
import javax.swing.undo.AbstractUndoableEdit;
 
69
import javax.swing.undo.CannotRedoException;
 
70
import javax.swing.undo.CannotUndoException;
 
71
import javax.swing.undo.UndoManager;
 
72
import javax.swing.undo.UndoableEdit;
 
73
import javax.swing.undo.UndoableEditSupport;
 
74
import org.netbeans.api.project.FileOwnerQuery;
 
75
import org.netbeans.api.project.Project;
 
76
import org.netbeans.modules.refactoring.api.AbstractRefactoring;
 
77
import org.netbeans.modules.refactoring.api.MoveRefactoring;
 
78
import org.netbeans.modules.refactoring.api.RenameRefactoring;
 
79
import org.netbeans.modules.refactoring.api.SafeDeleteRefactoring;
 
80
import org.netbeans.modules.refactoring.api.SingleCopyRefactoring;
 
81
import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation;
 
82
import org.netbeans.modules.refactoring.spi.Transaction;
 
83
import org.netbeans.modules.xml.refactoring.impl.UndoRedoProgress;
 
84
import org.netbeans.modules.xml.refactoring.spi.RefactoringUtil;
 
85
import org.netbeans.modules.xml.refactoring.spi.SharedUtils;
 
86
import org.netbeans.modules.xml.retriever.catalog.ProjectCatalogSupport;
 
87
import org.netbeans.modules.xml.retriever.catalog.Utilities;
 
88
import org.netbeans.modules.xml.xam.AbstractModel;
 
89
import org.netbeans.modules.xml.xam.Component;
 
90
import org.netbeans.modules.xml.xam.EmbeddableRoot;
 
91
import org.netbeans.modules.xml.xam.Model;
 
92
import org.netbeans.modules.xml.xam.ModelSource;
 
93
import org.netbeans.modules.xml.xam.Nameable;
 
94
import org.netbeans.modules.xml.xam.NamedReferenceable;
 
95
import org.netbeans.modules.xml.xam.Referenceable;
 
96
import org.netbeans.modules.xml.xam.dom.AbstractDocumentModel;
 
97
import org.netbeans.modules.xml.xam.dom.DocumentModel;
 
98
import org.netbeans.modules.xml.xam.locator.CatalogModel;
 
99
import org.netbeans.modules.xml.xam.locator.CatalogModelException;
 
100
import org.openide.DialogDisplayer;
 
101
import org.openide.NotifyDescriptor;
 
102
import org.openide.filesystems.FileObject;
 
103
import org.netbeans.modules.refactoring.spi.BackupFacility;
 
104
 
 
105
/**
 
106
 * Single transaction object, ensuring the order of refactoring change across
 
107
 * affected models.  Responsible for undo/redo of refactoring
 
108
 *
 
109
 * @author Sonali
 
110
 */
 
111
public class XMLRefactoringTransaction implements Transaction {
 
112
    
 
113
    AbstractRefactoring request;
 
114
    List<RefactoringElementImplementation> elements;
 
115
    List<XMLRefactoringPlugin> plugins;
 
116
    Referenceable target;
 
117
    private Map<Model,UndoManager> undoManagers;
 
118
     //For delete refactoring we need to save the model since it wont be available after delete for undo/redo
 
119
    Model targetModel;
 
120
    boolean isLocal = false;
 
121
    ModelSource movedTargetModelSource;
 
122
    Map<Model, Set<RefactoringElementImplementation>> modelsInRefactoring;
 
123
    private boolean commited = false;
 
124
    private UndoManager genericChangeUndoManager;
 
125
    Map<String, FileObject> targetModelRefs;
 
126
    ArrayList<BackupFacility.Handle> ids = new ArrayList();    
 
127
    private boolean useBackupFacility = false;
 
128
    
 
129
    
 
130
    
 
131
    /**
 
132
     * Creates a new instance of XMLRefactoringTransaction
 
133
     */
 
134
   
 
135
    public XMLRefactoringTransaction(Referenceable target, AbstractRefactoring req){
 
136
        this.target = target;
 
137
        this.elements = new ArrayList();
 
138
        this.plugins = new ArrayList();
 
139
        this.request = req;
 
140
        this.targetModel = SharedUtils.getModel(target);
 
141
              
 
142
    }
 
143
        
 
144
    /**
 
145
     * Commits the refactoring changes for all the models
 
146
     */   
 
147
    public void commit() {
 
148
        //when commit is called, there is no way of knowing if its for refactoring or a redo
 
149
        //since for rename redo, we use undoManagers, keep a flag and call redo when appropriate
 
150
        //System.out.println("COMMIT called");
 
151
        try {
 
152
            if(commited){
 
153
                                 
 
154
                    //even if we used BackupFacility, use the genericChangeUndoManager for redo since
 
155
                    //move/file rename refactoring involve more than just target rename/move
 
156
                    if (genericChangeUndoManager != null && genericChangeUndoManager.canRedo()) {
 
157
                        genericChangeUndoManager.redo();
 
158
                        
 
159
                    }
 
160
                    for (BackupFacility.Handle id:ids) {
 
161
                        try {
 
162
                            id.restore();
 
163
                        } catch (IOException ex) {
 
164
                            ex.printStackTrace();
 
165
                            throw (RuntimeException) new RuntimeException().initCause(ex);
 
166
                        }
 
167
                    }
 
168
               
 
169
            }  else{
 
170
               commited=true;
 
171
               //backup the files before doing refactoring
 
172
               this.backup();
 
173
               process();
 
174
             } 
 
175
              }catch (IOException ioe) {
 
176
            String msg = ioe.getMessage();
 
177
            NotifyDescriptor nd = new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE);
 
178
            DialogDisplayer.getDefault().notify(nd);
 
179
       }
 
180
        
 
181
    }
 
182
 
 
183
    /**
 
184
     * Rollbacks the refactoring changes for all the models
 
185
     */
 
186
    public void rollback() {
 
187
        
 
188
        UndoRedoProgress progress = new UndoRedoProgress();
 
189
        progress.start();
 
190
        try {
 
191
           //for target use the genericChangeUndoManager
 
192
            if (genericChangeUndoManager != null && genericChangeUndoManager.canUndo()) {
 
193
                genericChangeUndoManager.undo();                
 
194
            }            
 
195
            for (BackupFacility.Handle id:ids)  { 
 
196
                try {
 
197
                    id.restore();
 
198
                } catch (IOException ex) {
 
199
                    ex.printStackTrace();
 
200
                    throw (RuntimeException) new RuntimeException().initCause(ex);
 
201
                }
 
202
            } 
 
203
                  
 
204
        
 
205
      }finally {
 
206
           progress.stop();
 
207
      }
 
208
      
 
209
    }
 
210
    
 
211
    /** Registers the RefactoringElements for each plugin
 
212
     * @param plugin that found the usages
 
213
     * @param list List of refactoring usages 
 
214
     * 
 
215
     */
 
216
    public void register(XMLRefactoringPlugin plugin, List<? extends RefactoringElementImplementation> list){
 
217
        elements.addAll(list);
 
218
        plugins.add(plugin);
 
219
    }
 
220
    
 
221
    
 
222
    private synchronized void process() throws IOException {
 
223
        
 
224
       if(modelsInRefactoring == null)
 
225
           modelsInRefactoring = getModels();
 
226
        Set<Model> models = modelsInRefactoring.keySet();
 
227
        //put this code in shared utils
 
228
        Set<Model> excludedFromSave = RefactoringUtil.getDirtyModels(models, targetModel);
 
229
        GeneralChangeExecutor ce = new GeneralChangeExecutor();
 
230
        
 
231
        try {            
 
232
            if (! isLocal) {
 
233
                if(ce.canChange(request.getClass(), target)) {
 
234
                //if (target instanceof Model &&  ( (request instanceof RenameRefactoring) || (request instanceof MoveRefactoring)) ) {
 
235
                    //file refactoring
 
236
                    addUndoableListener(ce);
 
237
                    ce.doChange(request);
 
238
                }else {
 
239
                    addUndoableRefactorListener(targetModel);
 
240
                }
 
241
              doRefactorTarget();
 
242
               
 
243
               for (Model model:models) {
 
244
                   addUndoableRefactorListener(model);
 
245
                }
 
246
                
 
247
                for (XMLRefactoringPlugin plugin : plugins) {
 
248
                    plugin.doRefactoring(elements);
 
249
                }
 
250
            
 
251
                           
 
252
            } else { // isLocal
 
253
                //Model model = request.getTargetModel();
 
254
                if (targetModel != null) {
 
255
                    try {
 
256
                        addUndoableRefactorListener(targetModel);
 
257
                        targetModel.startTransaction();
 
258
                        doRefactorTarget();
 
259
                        //if the scope is local, then there should be only one plugin with only
 
260
                        //the usages in the target model
 
261
                         for (XMLRefactoringPlugin plugin : plugins) {
 
262
                             plugin.doRefactoring(elements);
 
263
                         }
 
264
                        
 
265
                    } finally {
 
266
                        if (targetModel.isIntransaction()) {
 
267
                            targetModel.endTransaction();
 
268
                            
 
269
                        }
 
270
                    }
 
271
                   
 
272
                }
 
273
            }
 
274
            
 
275
                       
 
276
        } catch (RuntimeException t) {
 
277
            //setStatus(Status.CANCELLING);
 
278
            throw t;
 
279
        } finally {
 
280
            if (! isLocal) {
 
281
                removeRefactorUndoEventListeners();
 
282
                removeUndoableListener(ce);
 
283
                
 
284
            }
 
285
        }
 
286
    }
 
287
    
 
288
       
 
289
    private synchronized void addUndoableRefactorListener(Model model) {
 
290
        if (undoManagers == null) {
 
291
            undoManagers = new HashMap<Model,UndoManager>();
 
292
        }
 
293
        // checking against source to eliminate embedded model case
 
294
        FileObject source = (FileObject) model.getModelSource().getLookup().lookup(FileObject.class);
 
295
        if (source == null) {
 
296
            throw new IllegalArgumentException("Could not get source file from provided model"); //NOI18N
 
297
        }
 
298
        for (Model m : undoManagers.keySet()) {
 
299
            FileObject s = (FileObject) m.getModelSource().getLookup().lookup(FileObject.class);
 
300
            if (source.equals(s)) {
 
301
                return;
 
302
            }
 
303
        }
 
304
        
 
305
        if (undoManagers.get(model) == null) {
 
306
            UndoManager um = new UndoManager();
 
307
            model.addUndoableRefactorListener(um);
 
308
            undoManagers.put(model, um);
 
309
        }
 
310
    }
 
311
    
 
312
     private synchronized void removeRefactorUndoEventListeners() {
 
313
        if (undoManagers == null) return;
 
314
        for(Model model : undoManagers.keySet()) {            
 
315
            model.removeUndoableRefactorListener(undoManagers.get(model));
 
316
        }
 
317
        
 
318
                  
 
319
    }
 
320
     
 
321
      public void doRefactorTarget() throws IOException {
 
322
          if (target instanceof Nameable  && request instanceof RenameRefactoring) {
 
323
            SharedUtils.renameTarget((Nameable) target, ((RenameRefactoring)request).getNewName());
 
324
        } else if (target instanceof NamedReferenceable && request instanceof SafeDeleteRefactoring) {
 
325
            SharedUtils.deleteTarget((NamedReferenceable) target);
 
326
        } else if(target instanceof Model) {
 
327
            //just do nothing
 
328
        }
 
329
      }
 
330
      
 
331
      
 
332
   /** Registers the embedded usages
 
333
     * @param list List of usages 
 
334
     * 
 
335
     */
 
336
      public void register(List<? extends RefactoringElementImplementation> list) {
 
337
          if(list != null && list.size()> 0 ){
 
338
              elements.addAll(list);
 
339
          }
 
340
      }
 
341
      
 
342
      public void setLocalScope() {
 
343
          isLocal = true;
 
344
      }   
 
345
      
 
346
      public boolean isLocal(){
 
347
          return isLocal;
 
348
      }
 
349
      
 
350
        
 
351
       
 
352
       public synchronized boolean canUndo() {
 
353
       if (undoManagers == null || undoManagers.isEmpty()) {
 
354
            return false;
 
355
        }
 
356
        for (UndoManager um : undoManagers.values()) {
 
357
            if (! um.canUndo()) {
 
358
                return false; 
 
359
            }
 
360
        }
 
361
        return true;
 
362
    }
 
363
       
 
364
       private void refactorFile() throws IOException {
 
365
           if(modelsInRefactoring == null)
 
366
               modelsInRefactoring = getModels();
 
367
           Set<Model> all = modelsInRefactoring.keySet();
 
368
           FileObject referencedFO = (FileObject) ((Model)target).getModelSource().getLookup().lookup(FileObject.class);
 
369
           assert referencedFO != null : "Failed to lookup for file object in model source"; //NOI18N
 
370
           RefactoringUtil.saveTargetFile((Model)target, all);
 
371
           if(request instanceof RenameRefactoring ) {
 
372
               referencedFO = SharedUtils.renameFile(referencedFO,((RenameRefactoring)request).getNewName());
 
373
               refreshCatalogModel(referencedFO);
 
374
           } else if (request instanceof MoveRefactoring ) {
 
375
               FileObject targetFolder = SharedUtils.getOrCreateFolder(((MoveRefactoring)request).getTarget().lookup(URL.class));
 
376
               //for move, we do the following
 
377
               //first in the target model, update any references to external models
 
378
               //then move the target model
 
379
               //please note -- we update and then move
 
380
               updateTargetModelReferences(targetModel, targetFolder);
 
381
               RefactoringUtil.saveTargetFile(targetModel, all);
 
382
               referencedFO = SharedUtils.moveFile(referencedFO, targetFolder);
 
383
               if(referencedFO != null) {
 
384
                   //we have a new file and new model source
 
385
                   //keep a reference since we need this do undo
 
386
                 this.movedTargetModelSource = Utilities.getModelSource(referencedFO, true); 
 
387
                 updateCatalogForTarget(this.movedTargetModelSource);
 
388
                 refreshCatalogModel(referencedFO);
 
389
               }
 
390
           } else if (request instanceof SingleCopyRefactoring ){
 
391
               FileObject targetFolder = SharedUtils.getOrCreateFolder(((SingleCopyRefactoring)request).getTarget().lookup(URL.class));
 
392
               
 
393
               String newName = ((SingleCopyRefactoring)request).getNewName();
 
394
               FileObject newFobj = SharedUtils.copyFile(referencedFO, targetFolder, newName);
 
395
               if(newFobj != null){
 
396
                   this.movedTargetModelSource = Utilities.getModelSource(newFobj, true);
 
397
                   //get the model from the plugin since the domain specific factory will create the model
 
398
                   Model newModel =null;
 
399
                   for(XMLRefactoringPlugin plugin:plugins){
 
400
                        newModel = plugin.getModel(movedTargetModelSource);
 
401
                        if(newModel != null){
 
402
                            break;
 
403
                        }
 
404
                   }  
 
405
                   if(newModel == null)
 
406
                       return;
 
407
                   
 
408
                   //get the file objects from old model
 
409
                   Collection<Component> refs = new ArrayList<Component>();
 
410
                   for(XMLRefactoringPlugin plugin:plugins){
 
411
                       refs.addAll(plugin.getExternalReferences(targetModel));
 
412
                   }
 
413
               
 
414
                   if(refs.size() > 0 ){
 
415
                       boolean startTransaction = ! newModel.isIntransaction();
 
416
                       
 
417
                       //map schemaLocation to components in the new copied model
 
418
                       Collection<Component> newRefs = new ArrayList<Component>();
 
419
                       for(XMLRefactoringPlugin plugin:plugins){
 
420
                               newRefs.addAll(plugin.getExternalReferences(newModel));
 
421
                       }
 
422
                       
 
423
                       Map<String, Component> map = new HashMap<String, Component>();
 
424
                       for(Component r: newRefs){
 
425
                            for(XMLRefactoringPlugin plugin:plugins){
 
426
                                  String location = plugin.getModelReference(r);
 
427
                                  if(location != null){
 
428
                                      map.put(location, r);
 
429
                                      break;
 
430
                                  }
 
431
                           }
 
432
                       }
 
433
                  
 
434
                       CatalogModel cat = (CatalogModel) (targetModel).getModelSource().getLookup().lookup(CatalogModel.class);
 
435
                       if (startTransaction) {
 
436
                           // ((Model)target).startTransaction();
 
437
                            newModel.startTransaction();
 
438
                       }
 
439
                       for(Component ref: refs){
 
440
                          try {
 
441
                              boolean flag = true;
 
442
                                  for(XMLRefactoringPlugin plugin:plugins){
 
443
                                  String location = plugin.getModelReference(ref);
 
444
                                  if(location != null){
 
445
                                     URI uri = new URI(location);
 
446
                                     ModelSource source = null;
 
447
                                     FileObject fobj = null;
 
448
                                     try {
 
449
                                         source = cat.getModelSource(uri);
 
450
                                         fobj = source.getLookup().lookup(FileObject.class);
 
451
                                     } catch (CatalogModelException e){
 
452
                                         //this means the model source could be in the same project 
 
453
                                         fobj = SharedUtils.getFileObject(targetModel, uri);
 
454
                                         //if we have a fobj, we can now have two cases
 
455
                                         //the refactoring target model is being moved within the same project
 
456
                                         //or the target model is being moved to a different project
 
457
                                         if(fobj != null)
 
458
                                             flag = SharedUtils.inSameProject(targetFolder, fobj);
 
459
                                     
 
460
                                    }
 
461
                                   if(fobj == null)
 
462
                                      break;
 
463
                                   Component comp = map.get(location);   
 
464
                                   if(flag){
 
465
                                       String newLocation = SharedUtils.getReferenceURI(targetFolder, fobj).toString();
 
466
                                       if(comp != null)
 
467
                                           plugin.setModelReference(comp, newLocation);
 
468
                                       
 
469
                                   }else {
 
470
                                       String newLocation = fobj.getURL().toString();
 
471
                                       if(comp != null)
 
472
                                           plugin.setModelReference(comp, newLocation);
 
473
                                   }
 
474
                                   break;
 
475
                           }
 
476
                        }
 
477
                      }catch (URISyntaxException e) {
 
478
                          //do nothing. dont update this model reference
 
479
                      }
 
480
                   }
 
481
                                
 
482
                  if (startTransaction && (newModel).isIntransaction()) {
 
483
                       newModel.endTransaction();
 
484
                 }
 
485
                 
 
486
               }
 
487
               RefactoringUtil.saveTargetFile(newModel, all);
 
488
              }
 
489
           }
 
490
   
 
491
       }
 
492
       
 
493
       private void refreshCatalogModel( FileObject referencedFO) {
 
494
        //   Map<Model, Set<RefactoringElementImplementation>> modelsInRefactoring = SharedUtils.getModelMap(elements);
 
495
           if(modelsInRefactoring == null) 
 
496
                modelsInRefactoring = getModels();
 
497
           boolean addedEntry = false;
 
498
           Set<Model> models = modelsInRefactoring.keySet();
 
499
           for (Model ug : models) {
 
500
            FileObject referencingFO = ug.getModelSource().getLookup().lookup(FileObject.class);
 
501
            ProjectCatalogSupport pcs = SharedUtils.getCatalogSupport(referencingFO);
 
502
            if (pcs == null) continue;
 
503
            Set<RefactoringElementImplementation> elems = modelsInRefactoring.get(ug);
 
504
            for (Component uc : getRefactorComponents(elems)) {
 
505
                String reference = getModelReference(uc);
 
506
                if (reference == null) continue;
 
507
                try {
 
508
                     if (pcs != null && pcs.removeCatalogEntry(new URI(reference))) {
 
509
                            pcs.createCatalogEntry(referencingFO, referencedFO);
 
510
                            addedEntry = true;
 
511
                        }
 
512
                   //special case for move/copy refactoring when a referencedFO is being moved to a subproject
 
513
                   //in this case, there is no catalogEntry and a new one needs to be created
 
514
                     if(pcs != null && !addedEntry){
 
515
                            Project targetProject = FileOwnerQuery.getOwner(referencedFO);
 
516
                            Project project = FileOwnerQuery.getOwner(referencingFO);
 
517
                           if( SharedUtils.getProjectReferences(project).contains(targetProject) ){
 
518
                               pcs.createCatalogEntry(referencingFO, referencedFO);
 
519
                           }
 
520
                        }
 
521
                  
 
522
                } catch(Exception ex) {
 
523
                    Logger.getLogger(SharedUtils.class.getName()).log(Level.FINE, ex.getMessage());
 
524
                }
 
525
            }
 
526
        }
 
527
       }
 
528
           
 
529
       private List<Component> getRefactorComponents(Set<RefactoringElementImplementation> elementImpls) {
 
530
            List<Component> ret = new ArrayList<Component>();
 
531
            for(RefactoringElementImplementation element:elementImpls){
 
532
                if(element.isEnabled())
 
533
                    ret.add( (Component)element.getLookup().lookup(Component.class));
 
534
            }
 
535
            return ret;
 
536
        }
 
537
        
 
538
        private String getModelReference(Component comp){
 
539
            String ref = null;
 
540
            for(XMLRefactoringPlugin plugin:plugins){
 
541
                ref = plugin.getModelReference(comp);
 
542
                if(ref != null)
 
543
                    return ref;
 
544
            }
 
545
            return ref;
 
546
        }
 
547
        
 
548
       private void undoRenameFile() throws IOException {
 
549
        CatalogModel cat = (CatalogModel) ((Model)target).getModelSource().getLookup().lookup(CatalogModel.class);
 
550
        FileObject fo = (FileObject) ((Model)target).getModelSource().getLookup().lookup(FileObject.class);
 
551
        if(request instanceof RenameRefactoring ) {
 
552
            String oldName = request.getContext().lookup(String.class);
 
553
            if(oldName == null || oldName.equals("")) {
 
554
                throw new IOException("Unable to undo refactoring. Cannot retrieve old file name"); //not i118N
 
555
            }
 
556
            fo = SharedUtils.renameFile(fo, oldName);
 
557
            refreshCatalogModel(fo);
 
558
        } else if(request instanceof MoveRefactoring) {
 
559
            URL url = ((MoveRefactoring)request).getContext().lookup(URL.class);
 
560
            if(url == null)
 
561
                throw new IOException("Unable to undo refactoring. Cannot retrieve original package location");
 
562
            FileObject origFolder = SharedUtils.getOrCreateFolder(url);
 
563
            //to undo the move, do the following
 
564
            // first get the model for the new file
 
565
            // update the external references, if any, in the new model
 
566
            // move the model back to the original location
 
567
            if(this.movedTargetModelSource != null){
 
568
                Model model = null;
 
569
                
 
570
                //get the model from the plugin since the domain specific factory will create the model
 
571
                for(XMLRefactoringPlugin plugin:plugins){
 
572
                    model = plugin.getModel(movedTargetModelSource);
 
573
                    if(model != null){
 
574
                        break;
 
575
                    }
 
576
                 }    
 
577
            
 
578
                //update embedded references to external models
 
579
                if(model!= null) {
 
580
                    if(modelsInRefactoring == null)
 
581
                        modelsInRefactoring = getModels();
 
582
                    Set<Model> all = modelsInRefactoring.keySet();
 
583
                    updateTargetModelReferences(model, origFolder);
 
584
                    RefactoringUtil.saveTargetFile(model, all);
 
585
                }
 
586
                
 
587
                //finally, move the file
 
588
                FileObject movedFile = movedTargetModelSource.getLookup().lookup(FileObject.class);
 
589
                fo = SharedUtils.moveFile(movedFile, origFolder);
 
590
                if(fo != null) {
 
591
                    ModelSource temp = Utilities.getModelSource(fo, true); 
 
592
                    model = null;
 
593
                    //after the file is moved, we need to do one final thing.
 
594
                    //for redo, we need to point to the right target model
 
595
                    //which is the newly moved file
 
596
                    for(XMLRefactoringPlugin plugin:plugins){
 
597
                        model = plugin.getModel(temp);
 
598
                        if(model != null){
 
599
                            targetModel = model;
 
600
                            break;
 
601
                        }
 
602
                    }
 
603
                 refreshCatalogModel(fo);
 
604
                   //addUndoableRefactorListener(movedTargetModelSource);
 
605
               }
 
606
            } else
 
607
                throw new IOException("Unable to undo Move Refactoring");
 
608
        } else if(request instanceof SingleCopyRefactoring){
 
609
            FileObject fobj = this.movedTargetModelSource.getLookup().lookup(FileObject.class);
 
610
            fobj.delete();
 
611
        }
 
612
        
 
613
    }
 
614
    
 
615
       
 
616
    public String refactorForPreview(Model model){
 
617
        try {
 
618
             if(modelsInRefactoring == null )
 
619
                 modelsInRefactoring = getModels();
 
620
             Model mod = null;
 
621
                     
 
622
             //This takes care of WSDL model with embedded schema imports
 
623
             if(model instanceof DocumentModel) {
 
624
                 Component c = ((DocumentModel)model).getRootComponent();
 
625
                 if(c instanceof EmbeddableRoot) {
 
626
                     if(  ( (EmbeddableRoot)c).getForeignParent() != null )
 
627
                        mod = ( (EmbeddableRoot)c).getForeignParent().getModel();
 
628
                     
 
629
                 }
 
630
             } 
 
631
             
 
632
            if(mod == null)
 
633
               mod = model;
 
634
             Set<RefactoringElementImplementation> elements = modelsInRefactoring.get(mod);
 
635
             ArrayList<RefactoringElementImplementation> elementsForRefactoring = new ArrayList<RefactoringElementImplementation>(elements);
 
636
             
 
637
             //for file rename, we dont need to refactor the target
 
638
             if( !( (target instanceof Model) && ( (request instanceof RenameRefactoring) || (request instanceof MoveRefactoring))) ) {
 
639
                 targetModel.startTransaction();
 
640
                 doRefactorTarget();
 
641
             }
 
642
            
 
643
             if(targetModel != mod) {
 
644
                 mod.startTransaction();
 
645
             }
 
646
             for (XMLRefactoringPlugin plugin : plugins) {
 
647
                 plugin.doRefactoring(elementsForRefactoring);
 
648
             }
 
649
         
 
650
             String refactoredString = ( (AbstractDocumentModel)mod).getAccess().getCurrentDocumentText(); 
 
651
             if( !(target instanceof Model && request instanceof RenameRefactoring)) {
 
652
                 ( (AbstractModel)targetModel).rollbackTransaction();
 
653
              }
 
654
            ((AbstractModel)mod).rollbackTransaction();
 
655
                       
 
656
            return refactoredString;
 
657
                  
 
658
        }  catch (Exception e){
 
659
            String msg = e.getMessage();
 
660
            e.printStackTrace();
 
661
       }
 
662
        return "";
 
663
      
 
664
    }
 
665
    
 
666
        
 
667
     private Map<Model, Set<RefactoringElementImplementation>> getModels(){
 
668
        Map<Model, Set<RefactoringElementImplementation>> results = new HashMap<Model, Set<RefactoringElementImplementation>>();
 
669
        for(RefactoringElementImplementation element:elements){
 
670
           Component comp = element.getLookup().lookup(Component.class);
 
671
           Model model = null;
 
672
           //First group the RE by Foreign Model, if no Foreign Model, then group by Model
 
673
           //This takes care of WSDL model with embedded schema imports
 
674
           if(comp.getModel() instanceof DocumentModel) {
 
675
               Component c = ((DocumentModel)comp.getModel()).getRootComponent();
 
676
               if(c instanceof EmbeddableRoot) {
 
677
                     if(  ( (EmbeddableRoot)c).getForeignParent() != null )
 
678
                        model = ( (EmbeddableRoot)c).getForeignParent().getModel();
 
679
                     
 
680
               }
 
681
           } 
 
682
           if(model == null)
 
683
               model = comp.getModel();
 
684
           if(model == null)
 
685
               continue;
 
686
           Set<RefactoringElementImplementation> elementsInModel = results.get(model);
 
687
           if(elementsInModel == null){
 
688
               elementsInModel = new HashSet<RefactoringElementImplementation>();
 
689
               elementsInModel.add(element);
 
690
               results.put(model, elementsInModel);
 
691
           } else
 
692
               elementsInModel.add(element);
 
693
        }
 
694
        return results;
 
695
    }
 
696
     
 
697
     
 
698
     public synchronized void redo() throws CannotRedoException {
 
699
             
 
700
            if(modelsInRefactoring == null)
 
701
              modelsInRefactoring = getModels();
 
702
            Set<Model> models = modelsInRefactoring.keySet();
 
703
            
 
704
            Set<Model> excludedFromSave = RefactoringUtil.getDirtyModels(models, targetModel);
 
705
            
 
706
            if (genericChangeUndoManager != null && genericChangeUndoManager.canRedo()) {
 
707
                genericChangeUndoManager.redo();
 
708
            }
 
709
            
 
710
            if(undoManagers != null ){
 
711
                for (UndoManager um : undoManagers.values()) {
 
712
                    while (um.canRedo()) {
 
713
                        um.redo();
 
714
                    }
 
715
                }
 
716
            }           
 
717
            //fix for issue 108512
 
718
            if(undoManagers != null) {
 
719
                Set<Model> mods = undoManagers.keySet();
 
720
                for(Model m:mods){
 
721
                    if(m instanceof AbstractDocumentModel)
 
722
                        ((AbstractDocumentModel)m).getAccess().flush();
 
723
                }
 
724
            }
 
725
 
 
726
            if (!isLocal) {
 
727
                RefactoringUtil.save(models, targetModel, excludedFromSave);
 
728
            }
 
729
        
 
730
    }
 
731
     
 
732
     public synchronized boolean canRedo() {
 
733
        if (undoManagers == null || undoManagers.isEmpty()) {
 
734
            return false;
 
735
        }
 
736
        for (UndoManager um : undoManagers.values()) {
 
737
            if (! um.canRedo()) {
 
738
                return false; 
 
739
            }
 
740
        }
 
741
        if (genericChangeUndoManager != null && ! genericChangeUndoManager.canRedo()) {
 
742
            return false;
 
743
        }
 
744
        return true;
 
745
    }
 
746
     
 
747
    private synchronized void addUndoableListener(GeneralChangeExecutor executor) {
 
748
        genericChangeUndoManager = new UndoManager();
 
749
        executor.addUndoableEditListener(genericChangeUndoManager);
 
750
    }
 
751
    
 
752
    private synchronized void removeUndoableListener(GeneralChangeExecutor exec) {
 
753
        if (! (exec instanceof GeneralChangeExecutor) || 
 
754
            genericChangeUndoManager == null || exec == null) {
 
755
            return;
 
756
        }
 
757
        
 
758
        exec.removeUndoableEditListener(genericChangeUndoManager);
 
759
    } 
 
760
    
 
761
    private void updateTargetModelReferences(Model model, FileObject targetFolder)throws IOException {
 
762
        //before moving the targetFile, check if the targetFile has any external model references
 
763
               targetModelRefs = new HashMap<String, FileObject>();
 
764
               Collection<Component> refs = new ArrayList<Component>();
 
765
               for(XMLRefactoringPlugin plugin:plugins){
 
766
                   refs.addAll(plugin.getExternalReferences(model));
 
767
               }
 
768
               
 
769
               if(refs.size() > 0 ){
 
770
                   boolean startTransaction = ! (model).isIntransaction();
 
771
                   CatalogModel cat = (CatalogModel) (model).getModelSource().getLookup().lookup(CatalogModel.class);
 
772
                   if (startTransaction) {
 
773
                       // ((Model)target).startTransaction();
 
774
                       model.startTransaction();
 
775
                   }
 
776
                   for(Component ref: refs){
 
777
                      try {
 
778
                          boolean flag = true;
 
779
                          for(XMLRefactoringPlugin plugin:plugins){
 
780
                             String location = plugin.getModelReference(ref);
 
781
                             if(location != null){
 
782
                                 URI uri = new URI(location);
 
783
                                 ModelSource source = null;
 
784
                                 FileObject fobj = null;
 
785
                                 try {
 
786
                                     source = cat.getModelSource(uri);
 
787
                                     fobj = source.getLookup().lookup(FileObject.class);
 
788
                                 } catch (Exception e){
 
789
                                     //this means the model source could be in the same project 
 
790
                                     fobj = SharedUtils.getFileObject(model, uri);
 
791
                                     //if we have a fobj, we can now have two cases
 
792
                                     //the refactoring target model is being moved within the same project
 
793
                                     //or the target model is being moved to a different project
 
794
                                     if(fobj != null)
 
795
                                         flag = SharedUtils.inSameProject(targetFolder, fobj);
 
796
                                     
 
797
                                 }
 
798
                               
 
799
                               if(fobj == null)
 
800
                                  break;
 
801
                               if(flag){
 
802
                                   String newLocation = SharedUtils.getReferenceURI(targetFolder, fobj).toString();
 
803
                                   plugin.setModelReference(ref, newLocation);
 
804
                                   if(!SharedUtils.inSameProject(targetFolder, fobj ))
 
805
                                       targetModelRefs.put(newLocation, fobj);
 
806
                               }else {
 
807
                                   String newLocation = fobj.getURL().toString();
 
808
                                   plugin.setModelReference(ref, newLocation);
 
809
                               }
 
810
                               break;
 
811
                           }
 
812
                        }
 
813
                      }catch (URISyntaxException e) {
 
814
                          //do nothing. dont update this model reference
 
815
                      }
 
816
                   }
 
817
                 
 
818
                
 
819
                     if (startTransaction && (model).isIntransaction()) {
 
820
                         model.endTransaction();
 
821
                     }
 
822
                 
 
823
               }
 
824
}
 
825
    
 
826
    private void updateCatalogForTarget(ModelSource target){
 
827
           FileObject referencingFO = target.getLookup().lookup(FileObject.class);
 
828
            ProjectCatalogSupport pcs = SharedUtils.getCatalogSupport(referencingFO);
 
829
            if (pcs == null)return;
 
830
            if(targetModelRefs == null || targetModelRefs.size() == 0)
 
831
                return;
 
832
            Set<String> keys = targetModelRefs.keySet();
 
833
            for(String key:keys){
 
834
                FileObject referencedFO = targetModelRefs.get(key);
 
835
                if(referencedFO != null) {
 
836
                    try {
 
837
                        pcs.createCatalogEntry(referencingFO, referencedFO);
 
838
                    }catch (CatalogModelException e){
 
839
                        
 
840
                    }catch (IOException e){
 
841
                        //do nothing; will result in broken reference
 
842
                    }
 
843
                }
 
844
            
 
845
        }
 
846
            
 
847
    }
 
848
    
 
849
    private void backup() throws IOException {
 
850
        boolean addedTarget = false;
 
851
        if(modelsInRefactoring == null)
 
852
              modelsInRefactoring = getModels();
 
853
            Set<Model> models = modelsInRefactoring.keySet();
 
854
            for(Model mod:models){
 
855
                FileObject fo = mod.getModelSource().getLookup().lookup(FileObject.class);
 
856
                ids.add(BackupFacility.getDefault().backup(mod.getModelSource().getLookup().lookup(FileObject.class)));
 
857
                //backup facility doesnt keep track of unique file objects. need to do it ourselves
 
858
                if(mod.equals(targetModel))
 
859
                    addedTarget = true;
 
860
            }
 
861
            GeneralChangeExecutor ce = new GeneralChangeExecutor();
 
862
            if(!ce.canChange(request.getClass(), target)  && !addedTarget) { 
 
863
                ids.add(BackupFacility.getDefault().backup(targetModel.getModelSource().getLookup().lookup(FileObject.class)));
 
864
            }           
 
865
    }
 
866
    
 
867
        
 
868
   class GeneralChangeExecutor  {
 
869
    private UndoableEditSupport ues;
 
870
    
 
871
    /** Creates a new instance of GenericChangeExecutor */
 
872
    public GeneralChangeExecutor() {
 
873
        ues = new UndoableEditSupport(this);
 
874
    }
 
875
    
 
876
    public <T extends AbstractRefactoring> boolean canChange(Class<T> changeType, Referenceable target) {
 
877
        if ( (changeType == RenameRefactoring.class || changeType == MoveRefactoring.class || changeType == SingleCopyRefactoring.class) && target instanceof Model) {
 
878
            return true;
 
879
        }
 
880
        return false;
 
881
    }
 
882
    
 
883
    /**
 
884
     * Perform the change specified by the refactor request.  Any errors that would
 
885
     * fail the overall refactoring should be reported throught #RefactoringRequest.addError
 
886
     * Implementation should quietly ignore unsupported refactoring type.
 
887
     */
 
888
    public void doChange(AbstractRefactoring request) throws IOException {
 
889
        if ((request instanceof RenameRefactoring) || (request instanceof MoveRefactoring) || (request instanceof SingleCopyRefactoring) ) {
 
890
            refactorFile();
 
891
            FileRenameUndoable ue = new FileRenameUndoable(request);
 
892
            fireUndoEvent(ue);
 
893
        }
 
894
    }
 
895
    
 
896
    
 
897
    
 
898
    public synchronized void addUndoableEditListener(UndoableEditListener l) {
 
899
        ues.addUndoableEditListener(l);
 
900
    }
 
901
    
 
902
    public synchronized void removeUndoableEditListener(UndoableEditListener l) {
 
903
        ues.removeUndoableEditListener(l);
 
904
    }
 
905
    
 
906
    protected void fireUndoEvent(UndoableEdit edit) {
 
907
            UndoableEditEvent ue = new UndoableEditEvent(this, edit);
 
908
            for (UndoableEditListener l:ues.getUndoableEditListeners()) {
 
909
            l.undoableEditHappened(ue);
 
910
            }
 
911
    }
 
912
    
 
913
     class FileRenameUndoable extends AbstractUndoableEdit {
 
914
        private static final long serialVersionUID = -1L;
 
915
        private AbstractRefactoring request;
 
916
        
 
917
        public FileRenameUndoable(AbstractRefactoring request) {
 
918
            this.request = request;
 
919
        }
 
920
        
 
921
        public void undo() throws CannotUndoException {
 
922
            try {
 
923
                undoRenameFile();
 
924
                super.undo();
 
925
            } catch(IOException ioe) {
 
926
                CannotUndoException cue = new CannotUndoException();
 
927
                cue.initCause(ioe);
 
928
                throw cue;
 
929
            }
 
930
        }
 
931
        
 
932
        public void redo() throws CannotRedoException {
 
933
            try {
 
934
                refactorFile();
 
935
                super.redo();
 
936
            } catch(IOException ioe) {
 
937
                CannotUndoException cue = new CannotUndoException();
 
938
                cue.initCause(ioe);
 
939
                throw cue;
 
940
            }
 
941
        }
 
942
    }
 
943
}
 
944
 
 
945
       
 
946
  }