concrete 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+