require 'fileutils' class WarningReverseBadOrMissingEnd < Warning end #Internal use. class LoadModelLazyWriter #Internal use. def initialize (fn,ft) @fileName=fn @fileType=ft @model=nil @usedResources=nil end def fileType if @fileType.nil? @fileType=FileType.getFileType(@fileName) end return @fileType end #Internal use. def protectedMode? return !@model.nil? end def [](key) if @model.nil? @model=Crdf_Repository.new @model.mtk_retrieveProtected(@fileName,fileType) end if @usedResources.nil? @usedResources=Array.new end ret=@model[key] @usedResources << key unless ret.nil? return ret end def unusedData return nil if @model.nil? ret=nil @model.each { |k,c| next if @usedResources.include?(k) # c is either a String or a bootstrap element. next if c.kind_of?(Mrdf_Resource) && c.ext_isBootstrap? ret=Array.new if ret.nil? ret << k } return ret end end module Mmtk_helper end module Mrdf_Resource include Mmtk_helper end class Crdf_Repository include Mmtk_helper end class Crdf_Resource include Mmtk_helper end module Mrdf_Repository include Mmtk_helper end module Mmtk_helper # Internal use. # # Used primarily by mtk server def mtk_stringWriteSession() fileName=context[:mtk_fileName,'string-file'] file=StringOutputStream.new ret=nil mtk_context(:mtk_out=>file,:mtk_fileName=>fileName) { @@mtk_out=context[:mtk_out] # perf yield ret=@@mtk_out.to_s } @@mtk_out=context.get(:mtk_out) # perf return ret end #mtk_writeSession temporary file suffix. TEMP_FILE_SUFFIX='.xmda_toolkit_temporary' @@max_file_logged=30 #le fichier fileName a ete genere pour la resource res attr_reader :fileGenerated #@fileGenerated=Array.new def notifyFileGenerated(fileName,res) #@fileGenerated << [ fileName , res ] end #Opens a file for writing. #Note: # Existing file is deleted # Directory are automatically created # Write is atomic. In case of failure existing file is not affected. # The name of the current file is available in context: context[:mtk_fileName] # # if context[:dryRun] is true, file writes will be skipped. # Note that reverse (file read) **will** be performed however. #Example: #mtk_writeSession('file1.java') { # write('this goes into file1.java') # mtk_writeSession('file2.java') { # write('this goes into file2.java') # puts context[:mtk_fileName] # } # write('this goes into file1.java') # } MTK_WRITE_SESSION_FILE_NAMES=Set.new def mtk_writeSession(fileName,fileType=nil) #if a file exist named fileName.mda, then write to this #file instead of fileName divert=fileName+'.mda' if (File.exists?(divert)) fileType=FileType.getFileType(fileName) if fileType.nil? fileName=divert end # Open or create session mtk_writeSessionNameClashErrorDetection(fileName.to_s) protectedReverse=LoadModelLazyWriter.new(fileName,fileType) FileUtils.mkdir_p(File.dirname(fileName)) notifyFileGenerated(File.expand_path(fileName),self) doNotOverwrite=mtk_autoNewFileCreationCheck(fileName) if context[:dryRun,false] file=NullFileWriter.instance #log.debug { "Simulating Write to #{fileName} (because context[:dryRun]==true)" } mtk_context(:mtk_out=>file,:mtk_fileName=>fileName,:protectedReverse=>protectedReverse) { @@mtk_out=context[:mtk_out] # perf yield } @@mtk_out=context.get(:mtk_out) # perf return end File.open(fileName+TEMP_FILE_SUFFIX,File::CREAT|File::TRUNC|File::RDWR, 0644) { |file| mtk_context(:mtk_out=>file,:mtk_fileName=>fileName,:protectedReverse=>protectedReverse) { #log.debug { "Writing #{fileName}" } @@mtk_out=context[:mtk_out] # perf yield } @@mtk_out=context.get(:mtk_out) # perf } #Temporary file has been corectly generated. unusedData=protectedReverse.unusedData if(!unusedData.nil?) log.info { "warning: saved code appended to #{fileName}.mtk_save" } msg='' File.open(fileName+'.mtk_save',File::CREAT|File::APPEND|File::RDWR, 0644) { |file| unusedData.each {|k| msg=msg+%{\n ** uri="#{k}"} file.write <0 break # exit while rescue => e retryCount=retryCount+1 #log.warn "#{e}" raise e if retryCount > 3 puts "Rename failed. Retrying (#{retryCount})" sleep(2) # end end return nil end #checks if fileName already exists def mtk_autoNewFileCreationCheck(fileName) doNotOverwrite=!context[:autoNewFileCreation,true] && !File.exists?(fileName) if doNotOverwrite # xmda is not allowed to create new file log.error { %{ ********* NEW FILE DETECTED ********** ** A new file needs to be added to your project: ** "#{fileName}" ** ** Generator has been halted to let ** you take proper actions such as: ** ** * Refactor your code if this new file is a renamed file ** * Create a blank file and Add this file to version control ** ** Model Element being processed is: ** #{mtk_object_message} ** ** Model element being processed is related to: ** #{mtk_related_message} ********* NEW FILE DETECTED ********** }} #exit(1) end return doNotOverwrite end #detects if fileName has already been used. #Logs an error if a clash is detected. def mtk_writeSessionNameClashErrorDetection(fileName) if(MTK_WRITE_SESSION_FILE_NAMES.include?(fileName.to_s)) log.error{ %{********* NAME CLASH DETECTED ********** #{fileName} ********* NAME CLASH DETECTED ********** ** This software has detected that a generated file ** is being overwritten in the same code generation session. ** This most likely is caused by a name clash in the source model. ** ** Filename being overwritten is ** "#{fileName}" ** Model Element being processed is: ** #{mtk_object_message} ** ** Model element being processed is related to: ** #{mtk_related_message} ********* NAME CLASH DETECTED ********** }} else MTK_WRITE_SESSION_FILE_NAMES<< fileName.to_s end end #writes before string, #yield block and then writes after. # #EXAMPLE. # encloseWrite("","") { write "Today news" } def encloseWrite(beforeStr,afterStr,stealth=false,&middleBlock) if(stealth) s=mtk_stss { yield } return false if s.nil? || s.empty? write(beforeStr.to_s) write(s) write(afterStr.to_s) return true else write(beforeStr.to_s) yield write(afterStr.to_s) return nil end end # Write string to the current file opened by mtk_writeSession. def write(str) if !@@mtk_out.nil? @@mtk_out.write(str) return end raise Warning.new(), <fileName) { File.open(fileName,'r') { |aFile| i=0 reading=false block_uri=nil buffer='' reverseMode=nil aFile.each_line { |line| i=i+1 if globalFile && !endFilesExtension if !endFilesName if line.include?('End_Files_Name') endFilesName=true else context[:reversedFiles].concat(line[0,line.size-1].split(',')) end else if line.include?('End_Files_Extension') endFilesExtension=true else context[:requireReverseExtensions].concat(line[0,line.size-1].split(',')) end end next end if !reading #return if !line.include?("PROTECTED REGION XMDA BEGIN") # optimisation ? m=fileType.re_begin.match(line) #log.debug {"beg match_ok=#{!m.nil?} line=#{line}"} next if m.nil? #raise Warning.new,"Error Reading Protected region header\n#{fileName}:line #{i}: #{line}" if m.nil? reading=true buffer='' linesep='' block_uri=fileType.unescape(m[2].to_s) #log.debug { "ma: #{m[0]} block_uri=#{block_uri}" } raise Warning.new,'Error Reading Protected region header nil uri' if block_uri.nil? case m[1] when 'yes' then reverseMode=true when 'no' then reverseMode=false else raise Warning.new,%{Bad reverseMode value in file #{fileName}. reverse="yes" or reverse="no" expected (got "#{m[1]}" line ##{i}).} end raise Warning.new,'Error Reading Protected region header nil uri' if reverseMode.nil? #log.debug { "!!!!!! block_ri=--#{block_uri}--" } else #log.debug "#{reading} line=#{line}" m=fileType.re_end.match(line) #log.debug "end match_ok=#{!m.nil?} line=#{line}" if m.nil? buffer=buffer.nil? ? line : "#{buffer}#{line}" next end block_uri_end=fileType.unescape(m[1].to_s) if block_uri_end!=block_uri raise WarningReverseBadOrMissingEnd.new, <xxxxxxx\n#{buffer}xxxxxxxxxx" if reverseMode buffer=buffer[0,buffer.size-1] unless buffer=='' if (!context[:hasGlobalReverse]) qualified_block_uri=mtk_qualifyBlockURI(block_uri) self[qualified_block_uri]=buffer else qualified_block_uri=block_uri context[:reverseGlobalMap][qualified_block_uri]=buffer end open('reverse.log','a') {|r| r.write("\n### reverse #{qualified_block_uri}\n") r.write( "#{buffer} #{buffer.length}") } #unless context[:logFileReverse].nil? else #log.debug "Reverse ignored #{context[:mtk_fileName]}!" end end } }} return true # un fichier a ete retro end def mtk_shouldAlreadyBeenReversed(fileName) return context[:requireReverseExtensions].include?(File.extname(fileName)) end end #module Mrdf_Resource #end module Mmtk_helper #_Resource REVERSE=true NOREVERSE=false WITHOUT_MARKER=true # mtk_protected is used when generated code can be modified by user and preserved by subsequent generation. # The code generated in the block passed as parameter will be generated encolosed between identifiable tags. # # NOTE: # The block is identified by the current rdf resource URI. # If more than one block is to be generated in one file, zoneId must be used. # # # # #Example 1: default # mtk_writeSession('file1.java') { # write("Package .... ;") # a.mtk_protected() { # write("default code to be redefined by user"); # } # b.mtk_protected() { # write("default code to be redefined by user"); # } # } #Example 2: initial mode is reverse # mtk_writeSession('file1.java') { # write("Package .... ;") # mtk_protected(REVERSE) { # write("default code to be redefined by user"); # } # } #Example 3: initial mode is noreverse # mtk_writeSession('file1.java') { # write("Package .... ;") # mtk_protected(NOREVERSE) { # write("default code to be redefined by user"); # } # } #Example 4: multiple-protected zone for a single object # mtk_writeSession('file1.java') { # write("Package .... ;") # a.mtk_protected(REVERSE,"a") { # write("default code to be redefined by user"); # } # a.mtk_protected(REVERSE,"b") { # write("default code to be redefined by user"); # } # } def mtk_protected(initialReverseMode=false,zoneId='0',noMarker=false, &block) #mbu=mtk_block_uri raise Exception.new('Bad parameter') if initialReverseMode!=false && initialReverseMode!=true mbu="#{rdf_uri}__#{zoneId}" qualified_uri=rdf_Repository.mtk_qualifyBlockURI(mbu) fileType=context[:protectedReverse].fileType str=nil if context[:hasGlobalReverse,false] str=context[:reverseGlobalMap][qualified_uri] else str=context[:protectedReverse][qualified_uri] end reverse= (!str.nil?) || initialReverseMode if noMarker # do not use marker for this item elsif(!reverse) && context[:skipNoReverseMarker,false] write(fileType.beginMarkerSkip(mbu,reverse)) else write(fileType.beginMarker(mbu,reverse)) end if str.nil? #log.debug "!!yield block_uri=#{mbu}" ret=yield write(ret) if ret.kind_of?(String) else write(str) end if noMarker write("\n") if !str.nil? && str!="" # do not use marker for this item elsif (!reverse) && context[:skipNoReverseMarker,false] write(fileType.endMarkerSkip(mbu)) else write(fileType.endMarker(mbu)) end end #Stream to String Session def mtk_stss(&block) #@rdf_Repository.mtk_stringWriteSession(&block) mtk_stringWriteSession(&block) end # same as #mtk_protected but for writes. def mtk_wprotected(initialReverseMode=false,zoneId='0',&block) mtk_protected(initialReverseMode=false,zoneId='0',&block) end # same as #mtk_protected but for string instead of writes. def mtk_sprotected(initialReverseMode=false,zoneId='0',&block) return mtk_stss { mtk_protected(initialReverseMode,zoneId,&block)} end #internal method. # #Returns file descriptor currently used by mtk_writeSession. def mtk_out return context[:mtk_out] end def mtk_annotation(initialReverseMode=false,zonesId=['annotations'],noMarker=false) #mbu=mtk_block_uri raise Exception.new('Bad parameter') if initialReverseMode!=false && initialReverseMode!=true if !noMarker revAnno='' del='' nothingReverse = true zonesId.each{ |zone| mbu="#{rdf_uri}__#{zone}" qualified_uri=rdf_Repository.mtk_qualifyBlockURI(mbu) if !context[:hasGlobalReverse,false] str=context[:protectedReverse][qualified_uri] else str=context[:reverseGlobalMap][qualified_uri] end reverse= (!str.nil?) || initialReverseMode if reverse nothingReverse = false revAnno+=del del=', ' if zone=='0' revAnno+='code' else if zone=='annoparam0' revAnno+='anno_param' else if zone=='custom_code' revAnno+='custom' else revAnno+=zone end end end end } if nothingReverse then revAnno = 'nothing' end write("\t") # good for methods and attributes write("@OntoMDE(reverse = { #{revAnno} }, uri = \"#{rdf_uri}\")\n") end end end #Internal use. # #Used by mtk_stringWriteSession. class StringOutputStream def initialize @str=nil end # Internal use. # # Simulates file open def open ; end # Internal use. # # Simulates file open def close ; end # Internal use. # # Appends str to internal steam buffer def write (str) #log.debug "Ajout #{str.to_s} !!" if @str @str="#{@str}#{str.to_s}" else @str=str.to_s end end # Internal use. # returns the internal stream buffer def to_s return @str end end module Mcore_Product end class Ccore_Product < Crdfs_Class include Mcore_Product def generate end end # a file writer that writes nowhere class NullFileWriter include Singleton def write(str) # does nothing ! end end TEMP_FILE_TO_ERASE=[] def mtk_addTempFileToDelete(fileName) TEMP_FILE_TO_ERASE<<(fileName.to_s) end def mtk_deleteTempFiles FileUtils.rm TEMP_FILE_TO_ERASE end