concrete 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. data/CHANGELOG +32 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +87 -0
  4. data/concrete/basic_inline_editor.js +73 -0
  5. data/concrete/clipboard.js +72 -0
  6. data/concrete/concrete.js +58 -0
  7. data/concrete/constraint_checker.js +297 -0
  8. data/concrete/editor.js +964 -0
  9. data/concrete/element_extension.js +68 -0
  10. data/concrete/external_identifier_provider.js +112 -0
  11. data/concrete/helper.js +63 -0
  12. data/concrete/identifier_provider.js +168 -0
  13. data/concrete/inline_editor.js +55 -0
  14. data/concrete/metamodel_provider.js +171 -0
  15. data/concrete/model_interface.js +429 -0
  16. data/concrete/scroller.js +106 -0
  17. data/concrete/selector.js +302 -0
  18. data/concrete/template_provider.js +141 -0
  19. data/concrete/ui/abstract_dialog.js +80 -0
  20. data/concrete/ui/concrete_ui.js +28 -0
  21. data/concrete/ui/create_module_dialog.js +55 -0
  22. data/concrete/ui/images/close.png +0 -0
  23. data/concrete/ui/images/document-new.png +0 -0
  24. data/concrete/ui/images/document-save.png +0 -0
  25. data/concrete/ui/images/edit-find-replace.png +0 -0
  26. data/concrete/ui/images/emblem-symbolic-link.png +0 -0
  27. data/concrete/ui/images/help-browser.png +0 -0
  28. data/concrete/ui/images/minus_11px.png +0 -0
  29. data/concrete/ui/images/plus_11px.png +0 -0
  30. data/concrete/ui/images/preferences-system.png +0 -0
  31. data/concrete/ui/images/process-stop.png +0 -0
  32. data/concrete/ui/images/system-search.png +0 -0
  33. data/concrete/ui/layout_manager.js +54 -0
  34. data/concrete/ui/module_browser.js +88 -0
  35. data/concrete/ui/module_editor.js +217 -0
  36. data/concrete/ui/open_element_dialog.js +90 -0
  37. data/concrete/ui/preferences_dialog.js +75 -0
  38. data/concrete/ui/proceed_dialog.js +52 -0
  39. data/concrete/ui/search_replace_dialog.js +323 -0
  40. data/concrete/ui/style.css +296 -0
  41. data/concrete/ui/toolbar.js +74 -0
  42. data/concrete/ui/workbench.js +165 -0
  43. data/doc/concrete_developers_guide.html +1054 -0
  44. data/doc/concrete_developers_guide.txt +502 -0
  45. data/doc/concrete_users_guide.html +694 -0
  46. data/doc/concrete_users_guide.txt +223 -0
  47. data/example/formula_editor/example_data/example1.json +11 -0
  48. data/example/formula_editor/formula_editor.html +83 -0
  49. data/example/formula_editor/sqrt_horz.png +0 -0
  50. data/example/formula_editor/sqrt_vert.png +0 -0
  51. data/example/formula_editor/style.css +31 -0
  52. data/example/metamodel_editor/edit.rb +54 -0
  53. data/example/metamodel_editor/example_data/formula_metamodel.json +18 -0
  54. data/example/metamodel_editor/example_data/meta_metamodel.json +22 -0
  55. data/example/metamodel_editor/example_data/statemachine_metamodel.json +32 -0
  56. data/example/metamodel_editor/metamodel_editor.html +120 -0
  57. data/example/metamodel_editor/metamodel_editor2.html +135 -0
  58. data/example/metamodel_editor/metamodel_editor3.html +151 -0
  59. data/example/metamodel_editor/style.css +8 -0
  60. data/example/metamodel_editor/style2.css +19 -0
  61. data/example/metamodel_editor/style3.css +35 -0
  62. data/example/minimal_editor/minimal_editor.html +43 -0
  63. data/example/statemachine_editor/example_data/example1.json +11 -0
  64. data/example/statemachine_editor/state_background.png +0 -0
  65. data/example/statemachine_editor/statemachine_editor0.html +55 -0
  66. data/example/statemachine_editor/statemachine_editor1.html +62 -0
  67. data/example/statemachine_editor/statemachine_editor2.html +103 -0
  68. data/example/statemachine_editor/style0.css +8 -0
  69. data/example/statemachine_editor/style1.css +32 -0
  70. data/example/statemachine_editor/style2.css +43 -0
  71. data/example/themes/cobalt.css +176 -0
  72. data/example/themes/dialog-error.png +0 -0
  73. data/example/themes/dialog-information.png +0 -0
  74. data/example/themes/dialog-warning.png +0 -0
  75. data/example/themes/dots_12px.png +0 -0
  76. data/example/themes/fold_button_dots_when_hidden.css +18 -0
  77. data/example/themes/fold_button_plus_minus.css +21 -0
  78. data/example/themes/fold_button_plus_when_hidden.css +18 -0
  79. data/example/themes/light_blue.css +177 -0
  80. data/example/themes/minus_11px.png +0 -0
  81. data/example/themes/minus_13px.png +0 -0
  82. data/example/themes/minus_9px.png +0 -0
  83. data/example/themes/plus_11px.png +0 -0
  84. data/example/themes/plus_13px.png +0 -0
  85. data/example/themes/plus_9px.png +0 -0
  86. data/example/themes/white.css +177 -0
  87. data/lib/concrete/concrete_syntax_provider.rb +63 -0
  88. data/lib/concrete/config.rb +36 -0
  89. data/lib/concrete/file_cache_map.rb +88 -0
  90. data/lib/concrete/index_builder.rb +108 -0
  91. data/lib/concrete/metamodel/concrete_mmm.rb +45 -0
  92. data/lib/concrete/metamodel/ecore_to_concrete.rb +80 -0
  93. data/lib/concrete/server.rb +92 -0
  94. data/lib/concrete/util/logger.rb +24 -0
  95. data/lib/concrete/util/string_writer.rb +17 -0
  96. data/lib/concrete/working_set.rb +41 -0
  97. data/rakefile +33 -0
  98. data/redist/prototype.js +4320 -0
  99. data/redist/scriptaculous/builder.js +136 -0
  100. data/redist/scriptaculous/controls.js +991 -0
  101. data/redist/scriptaculous/dragdrop.js +975 -0
  102. data/redist/scriptaculous/effects.js +1130 -0
  103. data/redist/scriptaculous/scriptaculous.js +60 -0
  104. data/redist/scriptaculous/slider.js +275 -0
  105. data/redist/scriptaculous/sound.js +55 -0
  106. data/redist/scriptaculous/unittest.js +568 -0
  107. data/test/concrete_test.rb +5 -0
  108. data/test/file_cache_map_test.rb +90 -0
  109. data/test/file_cache_map_test/testdir/fileA +1 -0
  110. data/test/index_builder_test.rb +68 -0
  111. data/test/index_builder_test/ecore_index.js +85 -0
  112. data/test/index_builder_test/ecore_index_expected.js +85 -0
  113. data/test/integration/external_elements_test.html +114 -0
  114. data/test/metamodel_test.rb +40 -0
  115. data/test/metamodel_test/concrete_mmm_expected.js +19 -0
  116. data/test/metamodel_test/concrete_mmm_generated.js +19 -0
  117. data/test/metamodel_test/concrete_mmm_regenerated.js +19 -0
  118. data/test/unit/external_identifier_provider_test.html +138 -0
  119. data/test/unit/identifier_provider_test.html +269 -0
  120. data/test/unit/metamodel_provider_test.html +318 -0
  121. data/test/unit/model_interface_test.html +257 -0
  122. data/test/unit/template_provider_test.html +171 -0
  123. data/test/unit/test.css +90 -0
  124. data/test/working_set_test.rb +54 -0
  125. data/test/working_set_test/file1.txt +0 -0
  126. data/test/working_set_test/file2 +0 -0
  127. data/test/working_set_test/subdir/file3.xml +0 -0
  128. metadata +201 -0
@@ -0,0 +1,36 @@
1
+ require 'yaml'
2
+
3
+ module Concrete
4
+
5
+ class Config
6
+
7
+ def initialize(filename)
8
+ @filename = filename
9
+ end
10
+
11
+ def loadValue(key)
12
+ config = loadConfig
13
+ config[key] if config.is_a?(Hash)
14
+ end
15
+
16
+ def storeValue(key, value)
17
+ config = loadConfig
18
+ config[key] = value
19
+ File.open(@filename, "w") do |f|
20
+ YAML.dump(config, f)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def loadConfig
27
+ if File.exist?(@filename)
28
+ YAML.load_file(@filename)
29
+ else
30
+ {}
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,88 @@
1
+ require 'digest'
2
+ require 'fileutils'
3
+
4
+ module Concrete
5
+
6
+ # Implements a cache for storing and loading data associated with arbitrary files.
7
+ # The data is stored in cache files within a subfolder of the folder where
8
+ # the associated files exists.
9
+ # The cache files are protected with a checksum and loaded data will be
10
+ # invalid in case either the associated file are the cache file has changed.
11
+ #
12
+ class FileCacheMap
13
+ # optional program version info to be associated with the cache files
14
+ # if the program version changes, cached data will also be invalid
15
+ attr_accessor :versionInfo
16
+
17
+ # +cacheDir+ is the name of the subfolder where cache files are created
18
+ # +postfix+ is an extension appended to the original file name in order to
19
+ # create the name of the cache file
20
+ def initialize(cacheDir, postfix)
21
+ @postfix = postfix
22
+ @cacheDir = cacheDir
23
+ end
24
+
25
+ # load data associated with file +keyPath+
26
+ # returns :invalid in case either the associated file or the cache file has changed
27
+ def loadData(keyPath)
28
+ cf = cacheFile(keyPath)
29
+ return :invalid unless File.exist?(cf)
30
+ result = nil
31
+ File.open(cf, "rb") do |f|
32
+ checksum = f.read(41)[0..39]
33
+ data = f.read
34
+ if calcSha1(data) == checksum
35
+ if calcSha1(keyData(keyPath)) == data[0..39]
36
+ result = data[41..-1]
37
+ else
38
+ result = :invalid
39
+ end
40
+ else
41
+ result = :invalid
42
+ end
43
+ end
44
+ result
45
+ end
46
+
47
+ # store data +valueData+ associated with file +keyPath+
48
+ def storeData(keyPath, valueData)
49
+ data = calcSha1(keyData(keyPath)) + "\n" + valueData
50
+ data = calcSha1(data) + "\n" + data
51
+ cf = cacheFile(keyPath)
52
+ FileUtils.mkdir(File.dirname(cf)) unless File.exist?(File.dirname(cf))
53
+ File.open(cf, "wb") do |f|
54
+ f.write(data)
55
+ end
56
+ end
57
+
58
+ # remove cache files which are not associated with any file in +keyPaths+
59
+ # will only remove files within +rootPath+
60
+ def cleanUnused(rootPath, keyPaths)
61
+ raise "key paths must be within root path" unless keyPaths.all?{|p| p.index(rootPath) == 0}
62
+ usedFiles = keyPaths.collect{|p| cacheFile(p)}
63
+ files = Dir[rootPath+"/**/"+@cacheDir+"/*"+@postfix]
64
+ files.each do |f|
65
+ FileUtils.rm(f) unless usedFiles.include?(f)
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def keyData(path)
72
+ File.read(path)+@versionInfo.to_s
73
+ end
74
+
75
+ def cacheFile(path)
76
+ File.dirname(path) + "/"+@cacheDir+"/" + File.basename(path) + @postfix
77
+ end
78
+
79
+ def calcSha1(data)
80
+ sha1 = Digest::SHA1.new
81
+ sha1.update(data)
82
+ sha1.hexdigest
83
+ end
84
+
85
+ end
86
+
87
+ end
88
+
@@ -0,0 +1,108 @@
1
+ require 'mmgen/metamodel_generator'
2
+ require 'andand'
3
+ require 'tempfile'
4
+
5
+ module Concrete
6
+
7
+ class IndexBuilder
8
+
9
+ include MMGen::MetamodelGenerator
10
+
11
+ def initialize(mm, options={})
12
+ @mm = mm
13
+ @identName = options[:identifierName] || "name"
14
+ @ignoreReferences = options[:ignoreReferences]
15
+ @moduleClassName = options[:moduleClassName] || "Module"
16
+ end
17
+
18
+ def indexMetamodel
19
+ @indexMM ||= buildIndexMetamodel
20
+ end
21
+
22
+ def buildIndex(root)
23
+ buildIndexInternal(root)
24
+ end
25
+
26
+ private
27
+
28
+ def buildIndexMetamodel
29
+ env = RGen::Environment.new
30
+
31
+ allContainersCache = {}
32
+ processedClasses = {}
33
+ referencedClasses = []
34
+ @mm.ecore.eAllClasses.eReferences.each do |r|
35
+ next if r.containment || r.derived
36
+ next if @ignoreReferences.andand.call(r)
37
+ next if processedClasses[r.eType]
38
+ processedClasses[r.eType] = true
39
+ allNamedSubclasses(r.eType).each do |c|
40
+ referencedClasses |= ([c] + allContainers(c, allContainersCache))
41
+ end
42
+ end
43
+
44
+ package = env.new(RGen::ECore::EPackage, :name => "IndexMetamodel")
45
+ indexElement = env.new(RGen::ECore::EClass, :name => "IndexElement", :ePackage => package)
46
+ env.new(RGen::ECore::EAttribute, :name => "name", :eType => RGen::ECore::EString, :eContainingClass => indexElement)
47
+ env.new(RGen::ECore::EReference, :name => "elements", :upperBound => -1, :containment => true, :eType => indexElement, :eContainingClass => indexElement)
48
+ env.new(RGen::ECore::EClass, :name => @moduleClassName, :eSuperTypes => [indexElement], :ePackage => package)
49
+
50
+ @mm.ecore.eAllClasses.each do |c|
51
+ if !c.abstract && referencedClasses.include?(c)
52
+ env.new(RGen::ECore::EClass, :name => c.name, :eSuperTypes => [indexElement], :ePackage => package)
53
+ end
54
+ end
55
+
56
+ loadIndexMetamodel(package)
57
+ end
58
+
59
+ def allNamedSubclasses(clazz)
60
+ if clazz.eAllStructuralFeatures.any?{|f| f.name == @identName}
61
+ [clazz] + clazz.eAllSubTypes
62
+ else
63
+ clazz.eAllSubTypes.select{|t| t.eAllStructuralFeatures.any?{|f| f.name == @identName}}
64
+ end
65
+ end
66
+
67
+ def loadIndexMetamodel(indexMMPackage)
68
+ mmFile = Tempfile.new("index_metamodel")
69
+ mmFile.close
70
+ generateMetamodel(indexMMPackage, mmFile.path)
71
+ containerName = "MMContainer"+self.object_id.to_s
72
+ eval("module "+containerName+"; end")
73
+ container = self.class.const_get(containerName)
74
+ container.module_eval(File.read(mmFile.path))
75
+ mmFile.unlink
76
+ container.const_get(indexMMPackage.name)
77
+ end
78
+
79
+ def buildIndexInternal(element)
80
+ return unless indexMetamodel.const_defined?(element.class.ecore.name)
81
+ return unless element.respond_to?(@identName)
82
+ ie = indexMetamodel.const_get(element.class.ecore.name).new
83
+ ie.name = element.getGeneric(@identName)
84
+ element.class.ecore.eAllReferences.select{|r| r.containment}.each do |r|
85
+ values = element.getGenericAsArray(r.name).compact
86
+ values.each do |v|
87
+ ie.addElements(buildIndexInternal(v))
88
+ end
89
+ end
90
+ ie
91
+ end
92
+
93
+ def containers(c)
94
+ types = c.eAllReferences.select{|r| r.eOpposite && r.eOpposite.containment}.eType
95
+ types + types.eAllSubTypes
96
+ end
97
+
98
+ def allContainers(c, cache)
99
+ return cache[c] if cache[c]
100
+ cache[c] = []
101
+ cons = containers(c)
102
+ cache[c] = cons | cons.collect{|cn| allContainers(cn, cache)}.flatten
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+
@@ -0,0 +1,45 @@
1
+ require 'rgen/metamodel_builder'
2
+
3
+ module Concrete
4
+
5
+ module Metamodel
6
+
7
+ # Concrete meta-metamodel
8
+ module ConcreteMMM
9
+ extend RGen::MetamodelBuilder::ModuleExtension
10
+ include RGen::MetamodelBuilder::DataTypes
11
+
12
+ FeatureKindType = Enum.new(:name => "FeatureKind", :literals =>[ :attribute, :reference, :containment ])
13
+
14
+ class Classifier < RGen::MetamodelBuilder::MMBase
15
+ abstract
16
+ has_attr 'name', String
17
+ end
18
+
19
+ class Class < Classifier
20
+ has_attr 'abstract', Boolean
21
+ end
22
+ Class.has_many 'superTypes', Class
23
+
24
+ class Datatype < Classifier
25
+ end
26
+
27
+ class Enum < Datatype
28
+ has_many_attr 'literals', String
29
+ end
30
+
31
+ class Feature < RGen::MetamodelBuilder::MMBase
32
+ has_attr 'name', String
33
+ has_attr 'kind', FeatureKindType
34
+ has_attr 'lowerLimit', Integer
35
+ has_attr 'upperLimit', Integer
36
+ has_one 'type', Classifier
37
+ end
38
+ Class.contains_many 'features', Feature, 'containingClass'
39
+
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
@@ -0,0 +1,80 @@
1
+ require 'rgen/transformer'
2
+ require 'rgen/ecore/ecore'
3
+ require 'concrete/metamodel/concrete_mmm'
4
+
5
+ module Concrete
6
+
7
+ module Metamodel
8
+
9
+ class ECoreToConcrete < RGen::Transformer
10
+ include RGen::ECore
11
+
12
+ def initialize(env_in, env_out, opts={})
13
+ super
14
+ @featureFilter = opts[:featureFilter]
15
+ end
16
+
17
+ def transform
18
+ uniqueClassNameCheck
19
+ trans(:class => EClass)
20
+ end
21
+
22
+ transform EClass, :to => ConcreteMMM::Class do
23
+ { :name => name,
24
+ :abstract => abstract,
25
+ :features => trans(eStructuralFeatures.reject{ |f|
26
+ (f.is_a?(RGen::ECore::EReference) && f.eOpposite && f.eOpposite.containment) ||
27
+ (@featureFilter && !@featureFilter.call(f)) }),
28
+ :superTypes => trans(eSuperTypes)
29
+ }
30
+ end
31
+
32
+ transform EStructuralFeature, :to => ConcreteMMM::Feature do
33
+ if eType.is_a?(EDataType) && !eType.is_a?(EEnum)
34
+ _type = primitiveType(eType)
35
+ else
36
+ _type = trans(eType)
37
+ end
38
+ { :name => name,
39
+ :type => _type,
40
+ :kind => (@current_object.is_a?(EAttribute) ? :attribute : (containment ? :containment : :reference)),
41
+ :lowerLimit => lowerBound,
42
+ :upperLimit => upperBound
43
+ }
44
+ end
45
+
46
+ transform EEnum, :to => ConcreteMMM::Enum do
47
+ { :name => name,
48
+ :literals => eLiterals.collect{|l| l.name}
49
+ }
50
+ end
51
+
52
+ def primitiveType(eType)
53
+ @primitiveType ||= {}
54
+ return @primitiveType[eType] if @primitiveType[eType]
55
+ if eType == EString
56
+ @primitiveType[eType] = @env_out.new(ConcreteMMM::Datatype, :name => "String")
57
+ elsif eType == EInt
58
+ @primitiveType[eType] = @env_out.new(ConcreteMMM::Datatype, :name => "Integer")
59
+ elsif eType == EFloat
60
+ @primitiveType[eType] = @env_out.new(ConcreteMMM::Datatype, :name => "Float")
61
+ elsif eType == EBoolean
62
+ @primitiveType[eType] = @env_out.new(ConcreteMMM::Datatype, :name => "Boolean")
63
+ else
64
+ # ignore unsupported datatype
65
+ end
66
+ end
67
+
68
+ def uniqueClassNameCheck
69
+ classNames = {}
70
+ @env_in.find(:class => EClass).each do |c|
71
+ raise "unqualified class names not unique, concrete does not support packages" if classNames[c.name]
72
+ classNames[c.name] = true
73
+ end
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
@@ -0,0 +1,92 @@
1
+ require 'webrick'
2
+
3
+ module Concrete
4
+
5
+ class Server
6
+
7
+ def initialize(workingSet, dataProvider, syntaxProvider, htmlRoot, options={})
8
+ @workingSet = workingSet
9
+ @dataProvider = dataProvider
10
+ @syntaxProvider = syntaxProvider
11
+ @htmlRoot = htmlRoot
12
+ @mutex = Mutex.new
13
+ @server = WEBrick::HTTPServer.new(:Port => (options[:port] || 1234))
14
+ @server.mount_proc("/") do |req, res|
15
+ handleRequest(req, res)
16
+ end
17
+ end
18
+
19
+ def start
20
+ @server.start do |sock|
21
+ @server.run(sock)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def handleRequest(req, res)
28
+ if req.path == "/"
29
+ editorHtml = File.read(@htmlRoot+"/editor.html")
30
+ editorHtml.sub!(/<!--\s+html templates\s+-->/, @syntaxProvider.selectedSyntax.htmlTemplates) if @syntaxProvider.andand.selectedSyntax.andand.htmlTemplates
31
+ res.body = editorHtml
32
+ elsif req.path =~ /^\/html\/(.*)/
33
+ File.open(@htmlRoot+"/"+$1, "rb") do |f|
34
+ res.body = f.read
35
+ end
36
+ elsif req.path =~ /^\/syntax\/(.*)/ && @syntaxProvider.selectedSyntax
37
+ File.open(@syntaxProvider.selectedSyntax.dir+"/"+$1, "rb") do |f|
38
+ res.body = f.read
39
+ end
40
+ elsif req.path =~ /^\/concrete\/(.*)/
41
+ File.open(File.dirname(__FILE__)+"/../../"+$1, "rb") do |f|
42
+ res.body = f.read
43
+ end
44
+ elsif req.path =~ /^\/doc\/(.*)/
45
+ File.open(File.dirname(__FILE__)+"/../../doc/"+$1, "rb") do |f|
46
+ res.body = f.read
47
+ end
48
+ elsif req.path == "/loadIndex"
49
+ @mutex.synchronize do
50
+ res.body = @dataProvider.getAllJsonIndex
51
+ end
52
+ elsif req.path == "/loadModule"
53
+ @mutex.synchronize do
54
+ res.body = @dataProvider.getJsonModel(req.query["module"])
55
+ end
56
+ elsif req.path == "/storeModule"
57
+ @mutex.synchronize do
58
+ identDelim = req.body.index("\n")
59
+ ident = req.body[0..identDelim-1]
60
+ body = req.body[identDelim+1..-1]
61
+ @dataProvider.setJsonModel(ident, body)
62
+ end
63
+ elsif req.path == "/createModule"
64
+ @mutex.synchronize do
65
+ @dataProvider.createModule(req.query["module"])
66
+ res.body = ""
67
+ end
68
+ elsif req.path == "/concreteSyntaxes"
69
+ res.body = @syntaxProvider.syntaxesAsJson
70
+ elsif req.path == "/setConcreteSyntax"
71
+ @syntaxProvider.selectSyntax(req.query["ident"])
72
+ elsif req.path =~ /\bmetamodel\.js/
73
+ @mutex.synchronize do
74
+ res.body = @dataProvider.metamodelAsJson
75
+ end
76
+ elsif req.path =~ /\bindex_metamodel\.js/
77
+ @mutex.synchronize do
78
+ res.body = @dataProvider.indexMetamodelAsJson
79
+ end
80
+ elsif req.path == "/exit"
81
+ @server.shutdown
82
+ elsif req.path =~ /favicon\.ico/
83
+ res.body = ""
84
+ else
85
+ # error
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+