001    package plugins.JavaRolePluginModule;
002    
003    import sharpster.common.*;
004    
005    public class Merger {
006        
007        /** Idé: Första skannet görs med utgångspunkt från parseträdet för partsFile.
008         *       Detta skan säkerställer att inga nya element har blivit tillförda
009         *       och att inga olovliga ändringar har skett.
010         *       Andra skannet görs med utgångspunkt från parseträdet för originalFile.
011         *       Detta skan införlivar de nya och modifierade kommentarerna samtidigt
012         *       som det säkerställer att inga element har tagits bort.
013         */
014        public static ResponseCollection mergePartsWithRoleDoc(SharedFile originalFile, SharedFile partsFile) {
015            //returvariabeln
016            ResponseCollection returnResponseCollection = new ResponseCollection();
017            
018            Body originalFileBody = originalFile.getBody();
019            Body partsFileBody = partsFile.getBody();
020            
021            /*test om någon av filern är binärfil (behövs kanske inte)
022            if (originalFileBody.isBinary() || partsFileBody.isBinary()) {
023                JavaRoleResponse res = new JavaRoleResponse();
024                res.setError(true);
025                res.setSubError(SubError.SYNTAX_ERROR);
026                res.setFilename(originalFile.getFileName());
027                returnResponseCollection.addResponse(res);
028                return returnResponseCollection;
029            }*/
030            
031            String originalFileStr = new String(originalFileBody.getData());
032            String partsFileStr = new String(partsFileBody.getData());
033            
034            ParseTree originalTree=new ParseTree(originalFileStr);
035            ParseTree partsTree=new ParseTree(partsFileStr);
036            if(originalTree.hasSyntaxError() || partsTree.hasSyntaxError()) {
037                JavaRoleResponse res = new JavaRoleResponse();
038                res.setError(true);
039                res.setSubError(SubError.SYNTAX_ERROR);
040                res.setFilename(originalFile.getFileName());
041                returnResponseCollection.addResponse(res);
042                return returnResponseCollection;
043                
044            }
045            
046            // om alla förändringar är "lagliga" ska orginalfilen uppdateras
047            if (Merger.performUpdateOfDoc(partsTree, originalTree, false)) {
048                System.out.println("OK, updating DOCCOMMENT:s.");
049                Merger.performUpdateOfDoc(originalTree, partsTree, true);
050            }
051            else {
052                System.out.println("Method, Class or variable header changed.");
053                JavaRoleResponse res = new JavaRoleResponse();
054                res.setError(true);
055                res.setSubError(SubError.UNAUTHORISED_CHANGE_IN_FILE);
056                res.setFilename(originalFile.getFileName());
057                returnResponseCollection.addResponse(res);
058                return returnResponseCollection;
059            }
060    
061            SharedFilesResponse res = new SharedFilesResponse();
062            FileCollection fc = new FileCollection();
063            SharedFile sf = new SharedFile(originalFile);
064            Body body = new Body();
065            
066            body.setData(originalTree.toString().getBytes());
067            sf.setBody(body);
068            fc.addFile(sf);
069            
070            res.setFiles(fc);
071            
072            returnResponseCollection.addResponse(res);
073            return returnResponseCollection;
074        }
075        
076        /** If change==false when this method ensures that no new methodhead,
077         *  methodbody, classhead, extra or variable has been added.
078         *  @sharpster.micke
079         */
080        private static boolean performUpdateOfDoc(ParseTree originalTree, ParseTree partsTree, boolean change) {
081            Entity currentOriginal=originalTree.getFirstChildOfFile();
082            Entity currentParts=partsTree.getFirstChildOfFile();
083            while(currentOriginal!=null) {
084                switch(currentOriginal.type) {
085                    case Entity.CLASSHEAD:
086                        //System.out.println("merger.classhead");
087                        currentParts=partsTree.getMatching(currentOriginal);
088                        if(currentParts==null) {
089                            System.out.print("Class head changed at : ");RMTester.print(currentOriginal);
090                            return false;
091                        }
092                        if(change) {
093                            mergeDocComment(originalTree,partsTree);
094                        }
095                        currentOriginal=originalTree.getNext();
096                        currentParts=partsTree.getNext();
097                        break;
098                    case Entity.METHODHEAD:
099                        //System.out.println("merger.methodhead");
100                        currentParts=partsTree.getMatching(currentOriginal);
101                        if(currentParts==null) {
102                            System.out.print("Method head changed at : ");RMTester.print(currentOriginal);
103                            return false;
104                        }
105                        if(change) {
106                            mergeDocComment(originalTree,partsTree);
107                        }
108                        currentOriginal=originalTree.getNext();
109                        currentParts=partsTree.getNext();
110                        break;
111                        
112                    case Entity.VARIABLE:
113                        //System.out.println("merger.variable");
114                        //System.out.println("c&wspace:"+'"'+currentOriginal.getContentAndWhitespace()+'"');
115                        //System.out.println("contents:"+'"'+currentOriginal.getContent()+'"');
116                        currentParts=partsTree.getMatching(currentOriginal);
117                        if(currentParts==null) {
118                            System.out.print("Variable changed at : ");RMTester.print(currentOriginal);
119                            return false;
120                        }
121                        currentOriginal=originalTree.getNext();
122                        //currentParts=partsTree.getNext();
123                        break;
124                    case Entity.ENDOFCLASSBODY:
125                        currentOriginal=originalTree.getNext();
126                        partsTree.getParent(); // CLASSBODY
127                        partsTree.getParent(); // same level as originalTree
128                        break;
129                    case Entity.CLASSBODY:
130                        if(currentParts!=null && currentParts.type==Entity.CLASSBODY) {
131                            // hoppa ner en nivå i båda träden
132                            currentOriginal=originalTree.getNext();
133                            currentParts=partsTree.getNext();
134                        }
135                        else {
136                            // klasskropp borta
137                            System.out.print("Body gone at: ");RMTester.print(originalTree.getPrevious());
138                            return false;
139                        }
140                        break;
141                    case Entity.METHODBODY:
142                        if(!change && currentParts.type!=Entity.SEMICOLON)
143                            return false;
144                        else {
145                            currentOriginal=originalTree.getNext();
146                            //currentParts=partsTree.getNext();
147                        }
148                        break;
149                    default:
150                        currentOriginal=originalTree.getNext();
151                        //currentParts=partsTree.getNext();
152                        break;
153                }
154            }
155            return true;
156        }
157        
158        /** Updates the doccomment in originalTree according to the
159         *  changes in partsTree. The cursor in originalTree should
160         *  be positioned at *HEAD. The cursor in originalTree will be
161         *  positioned at *HEAD after this method has been executed.
162         */
163        private static void mergeDocComment(ParseTree originalTree, ParseTree partsTree) {
164            Entity docOrig=originalTree.getPrevious(); // never null
165            Entity docParts=partsTree.getPrevious();   // never null
166            Entity currentOrig=docOrig;
167            Entity currentParts=partsTree.getNext();
168            if(docOrig.type==Entity.DOCCOMMENT) {
169                if(docParts.type==Entity.DOCCOMMENT) {
170                    //System.out.print("Changing:");RMTester.print(docOrig);
171                    currentOrig=originalTree.removeAndGetNext();
172                    //System.out.print("New:");RMTester.print(docParts);
173                    originalTree.insertBefore(docParts);
174                } else {
175                    //System.out.print("No DOCCOMENT in partsTree at:");RMTester.print(docOrig);
176                    currentOrig=originalTree.removeAndGetNext();
177                }
178            } else {
179                if(docParts.type==Entity.DOCCOMMENT) {
180                    currentOrig=originalTree.getNext();
181                    //System.out.print("Inserting:");RMTester.print(docParts);
182                    originalTree.insertBefore(docParts);
183                } else {
184                    currentOrig=originalTree.getNext();
185                }
186            }
187            //System.out.print("Orig at:");RMTester.print(currentOrig);
188            //System.out.print("Parts at:");RMTester.print(currentParts);
189            
190        }
191        
192        
193        public static ResponseCollection mergePartsWithRole(SharedFile originalFile, SharedFile partsFile, String role) {
194            //returvariabeln
195            ResponseCollection returnResponseCollection = new ResponseCollection();
196            
197            Body originalFileBody = originalFile.getBody();
198            Body partsFileBody = partsFile.getBody();
199            
200            /*test om någon av filern är binärfil (behövs kanske inte)
201            if (originalFileBody.isBinary() || partsFileBody.isBinary()) {
202                JavaRoleResponse res = new JavaRoleResponse();
203                res.setError(true);
204                res.setSubError(SubError.SYNTAX_ERROR);
205                res.setFilename(originalFile.getFileName());
206                returnResponseCollection.addResponse(res);
207                return returnResponseCollection;
208            }*/
209            
210            String originalFileStr = new String(originalFileBody.getData());
211            String partsFileStr = new String(partsFileBody.getData());
212            
213            ParseTree originalTree=new ParseTree(originalFileStr);
214            ParseTree partsTree=new ParseTree(partsFileStr);
215            
216            if(originalTree.hasSyntaxError() || partsTree.hasSyntaxError()) {
217                JavaRoleResponse res = new JavaRoleResponse();
218                res.setError(true);
219                res.setSubError(SubError.SYNTAX_ERROR);
220                res.setFilename(originalFile.getFileName());
221                returnResponseCollection.addResponse(res);
222                return returnResponseCollection;
223                
224            }
225            
226            Entity currentOriginal=originalTree.getFirstChildOfFile();
227            Entity currentParts=null;
228            boolean hasAccess=false;
229            
230            while(currentOriginal!=null) {
231                switch(currentOriginal.type) {
232                    case Entity.METHODHEAD:
233                        //System.out.println("merger.methodhead");
234                        if(hasAccess) {
235                            currentParts=partsTree.getMatching(currentOriginal);
236                            if(currentParts==null) {
237                                // överträdelsefel, header har blivit ändrad
238                                System.out.print("Method head changed at : ");RMTester.print(currentOriginal);
239                                JavaRoleResponse res = new JavaRoleResponse();
240                                res.setError(true);
241                                res.setSubError(SubError.UNAUTHORISED_CHANGE_IN_FILE);
242                                res.setFilename(originalFile.getFileName());
243                                returnResponseCollection.addResponse(res);
244                                return returnResponseCollection;
245                            }
246                            
247                            currentOriginal=mergeDocAndBodyAndGetNext(originalTree, partsTree);
248                        } else {
249                            currentOriginal=originalTree.getNextSkipHeadAndBody();
250                        }
251                        hasAccess=false;
252                        break;
253                    case Entity.CLASSHEAD:
254                        //System.out.println("merger.classhead");
255                        currentParts=partsTree.getMatching(currentOriginal);
256                        if(currentParts==null) {
257                            // @todo hantera överträdelsefel, header har blivit ändrad
258                            System.out.print("Class head changed at : ");RMTester.print(currentOriginal);                        JavaRoleResponse res = new JavaRoleResponse();
259                            res.setError(true);
260                            res.setSubError(SubError.UNAUTHORISED_CHANGE_IN_FILE);
261                            res.setFilename(originalFile.getFileName());
262                            returnResponseCollection.addResponse(res);
263                            return returnResponseCollection;
264                        }
265                        if(hasAccess) {
266                            currentOriginal=mergeDocAndBodyAndGetNext(originalTree,partsTree);
267                        }
268                        else{
269                            currentOriginal=originalTree.getNext();
270                            currentParts=partsTree.getNext();
271                        }
272                        hasAccess=false;
273                        break;
274                    case Entity.DOCCOMMENT:
275                        //System.out.println("merger.doccomment");
276                        if(currentOriginal.getContent().indexOf("@sharpster."+role)>0) {
277                            hasAccess=true;
278                        }else{
279                            hasAccess=false;
280                        }
281                        currentOriginal=originalTree.getNext();
282                        //System.out.println("hasAccess="+hasAccess);
283                        break;
284                    case Entity.ENDOFCLASSBODY:
285                        //System.out.println("merger.endofclassbody");
286                        hasAccess=false;
287                        currentOriginal=originalTree.getNext();
288                        partsTree.getParent(); // CLASSBODY
289                        partsTree.getParent(); // same level as originalTree
290                        break;
291                    case Entity.CLASSBODY:
292                        //System.out.println("merger.classbody");
293                        if(currentParts!=null && currentParts.type==Entity.CLASSBODY) {
294                            currentOriginal=originalTree.getNext();
295                            currentParts=partsTree.getNext();
296                        } else {
297                            JavaRoleResponse res = new JavaRoleResponse();
298                            res.setError(true);
299                            res.setSubError(SubError.UNAUTHORISED_CHANGE_IN_FILE);
300                            res.setFilename(originalFile.getFileName());
301                            returnResponseCollection.addResponse(res);
302                            return returnResponseCollection;
303                        }
304                        break;
305                    default:
306                        currentOriginal=originalTree.getNext();
307                        hasAccess=false;
308                        break;
309                }
310            }
311            SharedFilesResponse res = new SharedFilesResponse();
312            FileCollection fc = new FileCollection();
313            SharedFile sf = new SharedFile(originalFile);
314            Body body = new Body();
315            
316            body.setData(originalTree.toString().getBytes());
317            sf.setBody(body);
318            fc.addFile(sf);
319            
320            res.setFiles(fc);
321            
322            returnResponseCollection.addResponse(res);
323            return returnResponseCollection;
324        }
325        
326        private static Entity mergeDocAndBodyAndGetNext(ParseTree originalTree, ParseTree partsTree) {
327            
328            mergeDocComment(originalTree,partsTree);
329            
330            Entity bodyParts=partsTree.getNext(); // body
331            Entity currentParts=partsTree.getNext(); // next element after body
332            
333            Entity currentOrig=originalTree.getNext(); // body or semicolon
334            
335            //System.out.print("Inserting:");RMTester.print(currentParts);
336            originalTree.insertBefore(bodyParts);
337            
338            //System.out.print("Removing:");RMTester.print(currentOrig);
339            currentOrig = originalTree.removeAndGetNext();
340            
341            //System.out.print("Orig at:");RMTester.print(currentOrig);
342            //System.out.print("Parts at:");RMTester.print(currentParts);
343            
344            return currentOrig;
345        }
346    }
347    
348    
349