没有合适的资源?快使用搜索试试~ 我知道了~
理论计算机科学电子笔记102(2004)21-41www.elsevier.com/locate/entcsOCL 2.0-实现多元模型的标准大卫·阿克赫斯特1肯特大学计算机实验室英国屋大维·帕特拉斯科尤2肯特大学计算机实验室英国摘要OCL 2.0是OMG的约束语言的最新版本使用OCL作为伴随的约束和查询语言来使用这些语言建模是必不可少的。由于工具是为了支持建模语言而构建的,因此也有必要实现OCL。本文报告了我们在OMG的OCL标准的最新版本的基础上实现OCL的经验我们提供了一个高效的LALR语法解析的语言,并描述了一个架构,使语言被桥接到任何OO建模语言。此外,我们对标准中发现的问题和模糊之处提供反馈,并提出一些建议的解决方案。关键词:建模,语言,约束,文法,翻译器,编译器,解释器,解析器,桥。1介绍本文说明了我们如何实现了一个可执行版本的OCL,以这种方式,我们可以提供一个桥梁,以各种面向对象的元模型。这项工作的主要动机是提供一种工具,1电子邮件:D.H. kent.ac.uk2 电子邮件:O. kent.ac.uk1571-0661 © 2004 Elsevier B. V.根据CC BY-NC-ND许可证开放访问。doi:10.1016/j.entcs.2003.09.00222D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41在各种模型的总体上检查约束。最初的大部分工作是作为[1]的一部分进行的,最新版本和对OCL 2.0版的符合性是在肯特大学的肯特建模框架(KMF)项目[5]下完成的,涉及DSE4DS [3]和RWD [9]项目。我们提出了一个结构的OCL概念的模型,方便使用OCL在一些不同的元模型。一组精心指定的接口可以被定义为一个桥梁,它使OCL解析器,分析器,评估器和代码生成器的公共库我们已经为三个不同的元模型实现了桥梁,为Java、KMF和Eclipse建模框架(EMF)提供了OCL。我们的KMF实现将被更新为使用UML 2.0,当它被最终确定时,这里提出的架构促进了一个简单的更新路径。这个架构在OCL模型和它所连接的元模型之间提供了一个干净和定义良好的划分,同时仍然提供了必要的链接。实现这个库的经验向我们展示了OCL 2.0规范中存在的歧义、错误和缺失部分;我们强调并讨论了这些问题,并提出了一些解决问题的建议。我们已经产生了一个LALR语法的语法,适合输入自底向上的解析器生成器(CUP,YACC,BISON)。这样的语法不包含向前看或回溯。以前版本的解析器是基于LL(k)文法的,该文法源自原始的OCL标准。2实现结构我们的实现遵循翻译器的典型结构,包括4个阶段:词法分析,解析,语义分析和代码生成或评估。如图1所示。词法分析器和语法分析器从输入文本中生成一个抽象语法树(AST);语法错误由这些过程报告。 下一个过程,语义分析,需要输入AST和附加OCL表达式的用户语义分析会产生静态语义错误。最后,我们提供了两种合成选项,代码生成或使用解释器进行评估。D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-4123摘要语法树语义模型解释器语义分析器翻译到Java用户模型词法分析器错误管理器解析器源文本序列令牌Java代码OCL值图1实施结构我们将语义分析器的输出称为语义模型,因为该模型包含与表达式的语法相关的概念和引用来自用户模型的元素的概念。这些阶段中的每一个都涉及到与OCL标准中包含的规范相关的不同问题;我们将在以下章节中分别讨论每个阶段。3解析OCL标准并没有定义一个适合输入到解析器生成器的语法。该标准中规定的语法被归类为“Am-biased”语法。如果要实现该语言,则需要明确的语法。标准中指定的属性语法不适合作为解析器规范,因为它包含诸如解析规则之类的歧义:A::B::C这个表达式引用的是枚举文本还是类型的路径名标准中的语法通过使用上下文信息进行区分,而上下文信息在纯粹基于语法的分析(例如解析)期间不可用。 消除歧义的规则取决于来自环境的信息,即来自用户模型的语义信息和表达的上下文。24D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41|∗然而,没有句法上的差异。我们的语法与定义的语法是等价的,但是我们必须进行更改以启用确定性解析。这通常涉及为通过消歧规则(例如,枚举文字和路径名或属性调用的不同类型)区分的术语提供公共句法构造。附录A包含我们在实现中使用的LALR语法的EBNF。我们的语法不同于标准中定义的语法,因为它是明确的。最明显的区别是我们为OclExpression非终结符定义规则在每种编程语言中,运算符都有一个相关的优先级。编译器使用此属性来决定运算符的计算顺序。为了指定运算符的优先级,有两种选择:• 语法可以使用额外的非终结符在几个级别上进行结构化,或者• 在LR语法的情况下,优先级可以使用指令来指定我们决定使用第二个选项,因为它会给我们一个更小更快的解析器。这是因为第一种方法生成的语法有更多的非终结符,因此有更多的规则。例如,如果我们考虑一个带有+和 * 的算术表达式,根据1)的语法构建是E::=TXX::=λ J+JTXT::=FYY::= λ|JJFY使用第二种方法,可以定义以下语法:E::=EJJEE::=EJ+JE因此,我们没有使用额外的级别来分解OclExpression的非终结符,而是为每个运算符的使用指定了一个规则,并包含了运算符优先级的定义。以下小节讨论了我们在标准中所包含的语法规范中发现的问题3.1将类型解析为参数我们发现的一个问题是如何解析以下形式的表达式:D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-4125|expr.oclAsTypeOf(Type)如果Type是一个集合或元组类型,那么解析器将作为类型失败并不认为它们本身是有效的表达式。类型被引用为路径名,可以解析,并且可以在语义分析期间消除歧义。一个建议的解决方案是扩展文字表达式的定义,以包括集合和元组类型的语法3.2Iterator和Accumulator变量语法的另一个问题是迭代器和累加器变量的语法构造。这些变量具有相同的语法,并在迭代器和递归表达式中使用。根据[7]这样的表达式Set{1,2,3}−> select(x:x,y:y |x + y = 3)是迭代器表达式,设置{1,2,3,4,5,6}−>(e:; acc:= 0 |acc + e)是一个递归表达式。第一个表达式包含两个迭代器变量。第二个包含一个迭代器变量和一个累加器变量。该语言的语法被定义为,JJ符号,它提供了语法信息,即解析的名称是迭代器变量而不是表达式,位于迭代器变量的定义之后。这本身并不是一个问题,问题是由于多个迭代器变量可以被定义,用逗号分隔,有一个可选的类型定义和(语法上)可选的init表达式(尽管从语义的角度来看,init表达式是不允许的)。这些方面使得很难编写正确解析语言的规则;我们已经通过单独列出迭代器变量定义的数量和风格的变化来解决这个问题。如果使用替代分离器,这将变得更简单。一个可能的替代方案是一个分隔符;这样的分隔符在一个迭代器表达式中使用,因此它在迭代器表达式中不会不一致如果不沿着这些路线进行更改,语言就不能包含多迭代器变量- 两个以上-除非单独列出所有可能数字的选项。区分可能具有init表达式的语法变量声明构造和可能不具有init表达式的语法变量声明构造也可能是有利的26D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-414语义分析解析器生成一个抽象树-即输入的文本表达式的模型。这是一种语法中的规则与树中的节点之间存在直接关联的形式。AST纯粹是语法的抽象表示,被建模为树。在解释语法的含义之前,我们必须为表达式提供语义上下文。这个上下文包括两个部分:UML(或其他)用户模型,在这个模型上解释表达式;以及进入这个模型的入口点--即self变量的类型。分析器的工作是将AST映射到包含与上下文模型相关的语义信息的表达式的模型上,并报告在OCL标准中定义的模型,被错误地命名为抽象模型(ASM),就是这样一个模型。我们觉得这个模型的命名有误导性;它不包含纯粹的抽象语义信息,它包含语法节点和语义节点的混合。语义节点是包含与用户模型和表达的上下文相关的信息的节点我们建议使用另一个名称。由于该模型包含引用用户定义的上下文模型的语义信息,因此我们将其称为OCL语义模型。(Not与语义模型相混淆,这是本文中未涉及的其他我们的分析器执行两个工作:它将基于字符串的路径名映射到上下文模型中的类型,属性和方法,并将OCL特定操作映射到适当的语义模型构造。映射到语义模型构造是根据标准中定义的消歧规则执行的,映射到用户模型元素是通过在环境类上定义的操作执行的,也在标准中定义。4.1语义模型标准中定义的语义模型(或ASM)可以分为三组类:(i) 那些定义OCL概念的人。(ii) 那些引用来自UML元模型的概念(iii) 定义标准库的类型系统我们进一步将(i)中的概念分为与上下文有关的概念表达式和处理表达式中概念的表达式D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-4127在标准中,(ii)中的类通过定义来区分,它们来自UML元模型中的各种包,并且它们另外被着色为白色而不是灰色。我们将这些类重新定义为一个名为bridge的包的成员。它们保持与以前相同的名称,但是应该考虑映射到UML模型中的类,而不是直接从UML模型中的类图2 OCL语义模型概述(iii)中的类定义了标准库的类型系统;它们定义了库中包含的类型以及对这些类型可用的操作。我们把这些类放到一个单独的包中,因为它们不构成语义模型的一部分,尽管它们是语义模型所需要的,并且确实构成了OCL的定义语言语义我们将这些类集划分为包,如图2所示。为了将我们的OCL库映射到不同的模型上,有必要提供桥类的不同实现;其中许多可以是所提供的公共实现的扩展。在下面的小节中,我们将展示桥、上下文和类型包的内容,因为它们是标准中描述的类的添加或变体。表达式包包含标准中定义的类,除了与ModelPropertyCall表达式相关的类,我们也在下面的小节中进行了修改和展示28D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-414.2桥图3网桥类桥包中的类(图3)是那些必须由任何模型支持的类,希望在这些模型上解释OCL表达式。这些类共同提供上下文信息,使OCL表达式能够被计算。它们很容易从UML1.X元模型映射到类,因为这是OCL最初设计的模型。然而,我们已经成功地将类映射到Java的元模型和与IBM的Eclipse建模框架相关联的ECore元模型当类的规范最终确定时,我们看到将类映射到UML2.0元模型或MOF元模型没有问题类上 的操作和 属性是在 OCL 2.0 标准 中包含的 消除歧义 规则和Environment类上的操作定义D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41294.3类型标准中包含两个版本的OCL类型模型;一个是标准库定义的一部分,另一个是标准库定义的一部分。的语义模型。这两种类型的模型并不完全一致。我们合并了来自两个模型的信息,以提供一致的信息(图4)。图4 OCL类型标准库中定义的对象反映了这里定义的类型层次结构。主要变化是:• 包含OclType对象的类型。30D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41• 对层次结构的更改,以提供此模型与标准库对象的类型层次结构之间的• 添加OclAnyType。• 将TuplePart添加到TupleType。根据OCL 2.0的建议,集合和元组类型不被认为是OclAny的子类型。这意味着特定于OclAny的操作不能应用于元组和集合类型的实例。 我们没有找到任何理由为什么集合和元组类型不能被认为是OclAny的子类型,事实上,我们发现如果我们要使它们能够被类型转换,它们是必要的。例如,考虑一个Animal类型的Set,我们知道它只包含Dog类型的对象,我们可能希望对Set执行强制转换,如下面的表达式所示Set{rover,fido,fluffy}.oclAsType(Set(Dog))图5 OCL表达式上下文D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-4131这样的表达式目前在语法上(参见上文)或语义上都不被接受,因为OclAny操作不能用于集合。将元组和集合类型视为OclAny的子类型将提高OCL的表达能力和可用性。如果OCL中没有这样的功能,用户将不得不使用其他语法结构来获得相同的效果(例如,在上面的集合上重复并转换每个元素)。4.4上下文标准中给出了上下文定义的具体语法,但没有提供用于这种上下文的语义模型。图5中的模型是我们的实现所使用的模型。4.5表达式图6模型调用表达式除了ModelPropertyCall周围的类(图6)之外,表达式的模型按照标准中的定义使用。从OCL语义的角度来看,我们认为没有必要区分属性和关联端,因此我们将它们组合到一个类PropertyCall中。这个类也包括静态属性,所以表达能力不会降低。此外,类ModelPropertyCall被定义为Operations和新类PropertyCall的超类型;操作调用不是属性调用,因此我们认为 它应该直接从CallExp派生,因此类ModelPropertyCall变得多余。32D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41关于表达式包中的类还有一个注释; Let表达式在语法上被定义为一个语句序列,但在语义模型中被定义为嵌套在彼此内部的表达式。我们认为,这两种办法都是可以接受的,但它们应该是一致的。4.6环境Environment类的规范(在标准中)缺少了一些在标准中其他地方使用或引用的东西;有些是完全缺少的,有些是在类图中缺少的• 从环境到其父环境的关联• 操作lookupImplicitSourceForOperation、lookupPathName和addEnvironment我们在图7中显示了一个更完整的规范 我们还添加了一个方便的方法addVariableDeclaration;虽然addElement可以用来添加VariableDeclaration,但这不是必需的,这个操作避免了在将其添加到环境之前构造VariableDeclaration的需要。Environment 操 作 的指定使用了桥类上的各种方法;我们已经将这些操作添加到类中,如前一节关于桥类的内容所示。图7环境类的规范D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41335合成OCL的语义似乎定义得很好,我们在实现评估和代码生成过程方面遇到了一些问题。这两个过程都是作为语义模型上的访问者实现的。6OCL标准库我们的OCL标准库的实现构建在java.lang和java.util包中的我们在标准库类的实现方面遇到了一些当比较集合时(“=”的实现OclAny操作的实现依赖于支持桥包的模型的实现。我们的实现提供了一种工具,可以根据具体的桥实现来调整OclAny操作的评估,以应用于OclModelElements有一个关于OclVoid类(和未定义对象)的实现的问题;类型层次结构声明该类扩展(除了其他类型之外)所有集合类型。不幸的是,一些集合类型的方法具有相同的签名,但返回类型不同。目前我们还没有令人满意的解决方案,如果语义需要未定义的值,我们的实现将返回java如果需要,这些空值将由求值器映射到未定义的值。关于OclType有一些不明确之处。该标准在第3节中指出,类已经被删除,但它出现在标准库类和操作的定义中。我们选择包含OclType类,因为它对于诸如oclAsType之类的操作是必要的。我们还发现,在整个标准中,有许多操作被用于OCL类型,而这些操作并没有出现在标准库的定义中。例如,Sequence对象上的操作尾7桥接实现桥类的主要目的是提供OCL表达式和模型之间的链接,因此,根据我们用来实现桥的元模型,桥类的实现会有所不同,所涉及的问题也会有所不同。34D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41下面的小节讨论了与我们的三个桥实现中的每一个相关的问题。这些桥实现中的每一个都为枚举、空间、操作和属性类提供支持。其他桥类的实现对这三个类都是通用的,我们怀疑对大多数桥实现都是通用的7.1KMF的OCLKMF 2.0版基于UML1.4元模型。KMF使用UML 1.4 XMI文件来构建模型实现;我们希望将此实现用作OCL表达式的用户模型。为了获得正确的类型信息,无论模型实现细节如何,KMF桥实现都从用于存储模型信息的同一个XMI文件中获取所有信息,并生成实现模型的Java代码。该文件用于填充UML 1.4元模型的实现,该元模型用作桥类的底层实现7.2EMF的OCLEclipse建模框架(EMF)是IBM”EMF对于那些已经接受了面向对象建模思想的人来说,EMF可以帮助您快速地将模型转换为高效、正确和易于定制的Java代码EMF代码生成基于一个名为ECore的元模型(图8);正如您所看到的,这与UML元模型之间有相似之处。EMF生成的java代码携带了来自定义模型的所有信息(与KMF不同),即可以从每个实例化用户模型类的对象访问ECore类的实例。因此,桥类的实现是通过将调用转发到适当的ECore类来实现的。D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-4135图8 ECore模型(取自EMF概述)ECore模型和UML元模型之间的相似性意味着在提供桥接实现方面没有困难。唯一的问题是集合类的使用。EMF使用了一个EList实现和java.util.List的扩展来处理所有类型的集合。这个类有一个isUnique属性,用于区分具有Setlike属性的集合和不具有Set like属性的集合。Sequences和Bags之间或者Sets和OrderedSets之间没有明显的区别-所有的集合都是有序的;然而,这并没有证明会在构建桥梁时造成问题,但是必须记住,当从用户模型中获取集合属性时,总是会获得Sequence或OrderedSet7.3OCL for Java最有问题的桥实现是Java的桥实现Java没有提供创建枚举的显式机制;它没有提供类型化集合类;它的包概念与UML包概念不java的响应能力已经被证明是形成我们的桥实现的关键。36D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41枚举我们以两种方式之一来标识枚举。 通过在预先实例化的枚举列表中查找枚举,或者通过测试类是否扩展了java.util.Enumeration。这是对java.util.Enumeration类的轻微误用,但它提供了一个很好的解决方案。这样的枚举被假定为是以每个枚举文字作为枚举类的静态成员和该类的实例来实现的。命名空间命名空间的问题在于Java包是由它们的完整包名单独标识的。虽然看起来支持子包的概念,但java的反射特性并不支持这种子包关系。因此,要通过名称查找命名空间的所有元素,我们首先尝试找到一个具有元素名称和当前命名空间的完整路径名的Java类;如果失败,我们假设该名称是一个子命名空间,创建适当的子命名空间对象,并返回子命名空间。 这不一定是最好的方法,但似乎在大多数情况下都有效操作我们简单地使用反射来获取操作的java签名,并将其转换为桥类的正确表示性能我们假设每个属性都实现了标准的java get/set方法。桥实现只是将属性的名称大写,添加一个类型集合要为属性类型和操作返回类型构造正确的OCL类型化集合类型,需要获取有关集合类型的额外信息Java集合不携带这些信息。我们提供了两个选项;一个是预先实例化一个列表,将属性和操作名称映射到作为集合元素类型的Java类;或者当属性或操作具有集合作为其返回类型时,可以添加一个静态final字段反射操作用于在需要时查找该字段。D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41378相关工作有许多CASE工具支持UML图的绘制以及代码生成和逆向工程等功能。然而,在这些工具中很少发现对OCL和模型之间的转换和映射的支持为了提供对OCL的支持,CASE工具应该完成几项任务。例如,OCL构造的语法分析和报告语法错误的精确机制有助于编写语法正确的OCL语句。下一步可能是语义分析器,它应该报告尽可能多的错误,以帮助用户开发可靠的OCL代码。如果该工具同时支持解释器和编译器,则用户可以选择最佳方法以获得高质量的软件。OCL的第一个可用工具可能是由IBM的OCL作者开发的解析器,现在由Klasse Objecten维护。解析器使用[6]中描述的语法。另一个工具集是在德累斯顿工业大学开发的[4]。该工具的一部分已与开源CASE工具Argo集成[2]。[10]包含了一个OCL解释器的描述。它部分基于描述OCL抽象语法的OCL元模型。[8]也为OCL提供了一个很好的实现。9结论自从OCL第一次被添加到UML中以来,我们一直在尝试OCL的实现。这是我们的意见,该语言是无价的OMG建模环境的一部分,但我们认为,这是必要的,该语言的标准化过程的一部分,以避免我们发现的歧义和不一致的实现。我们的经验说明了标准需要改进的许多方面,我们提供了解决其中一些改进的想法。特别是,我们建议需要一个语言的参考实现,以改进标准中包含的定义9.1不支持的概念我们的实现目前不完全支持以下结构:• hasSent和message运算符• 上下文,而不是inv:• OclState、OclMessage类型• @前引用38D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41引用[1] 阿克赫斯特湾H、“模型翻译:基于UML的规范技术和主动实现方法”,博士。论文,计算系,肯特大学坎特伯雷分校,坎特伯雷,2000。[2] ArgoUML,具有认知支持的UML设计工具,URL:http://www.argouml.org。[3] DSE4DS团队,分布式系统设计支持(DSE4DS),URL:http://www.cs.kent.ac.uk/projects/dse4ds/index.html。[4] 德穆特湾,H. Hussman,F. Finger,支持OCL的工具集的模块化架构。在埃文斯A.,S. Kent和B. UML 2000:统一建模语言。推进标准。第三次国际会议,约克,英国,2000年10月,LNCS会议记录第1939(2000)卷,第440-450页,Springer 2000。[5] KMF-team,Kent Modeling Framework(KMF),URL:http://www.cs.kent.ac.uk/projects/kmf.[6] 对象约束语言规范。在OMG Uni Fied Modeling Language Specification,Version 1.3,1999年6月[19],第7章。[7] 对象管理组,统一建模语言2.0-对象约束语言2.0提案,URL:http://www.omg.org。[8] 对象约束语言评估员,信息学研究实验室,大学宝贝- Bolyai,克卢日,罗马尼亚.[9] RWD团队,用图表推理(RWD),URL:http://www.cs.kent.ac.uk/projects/rwd。[10] Gogolla M.,M.,Richters M.,OCL的元模型。在法兰西河。关于Rumpe B编辑。UML'99-统一建模语言超出标准。第二次国际会议,柯林斯堡,CO,美国,1999年10月28-30日,会议记录,LNCS第1723卷(1999年),第156-171页,Springer 1999。附件ApackageDeclaration::=contextDeclList::= contextDeclaration*contextDeclaration::= propertyContextDeclcontextDeclaration::= classi filerContextDeclcontextDeclaration::= operationContextDeclpropertyContextDecl::= 'context' pathname simpleName ':' typeinitOrDerValue+initOrDerValue::=operationContextDecl::=D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-4139−||||−prePostOrBodyDecl::=variableDeclarationList::= variableDeclaration(type::= pathnametype::=collectionTypetype::= tupleTypecollectionType::= collectionKindtupleType::=oclExpression::=oclExpression::= oclExpression '[' argumentList ']'encrypt'@' 'pre'] oclExpression::= oclExpression '.'simpleNameoclExpression::= oclExpressionoclExpression::= oclExpressionoclExpression::= oclExpressionoclExpression::= oclExpressionoclExpression::=oclExpression::= oclExpressionoclExpression::= 'if' oclExpression 'then' oclExpression 'else' oclExpression'endif'oclExpression::= oclExpression40D. Akehurst,O. Patrascoiu /理论计算机科学电子笔记102(2004)21-41||||||||----||||oclExpression::= oclExpression
下载后可阅读完整内容,剩余1页未读,立即下载
cpongm
- 粉丝: 4
- 资源: 2万+
上传资源 快速赚钱
- 我的内容管理 收起
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
会员权益专享
最新资源
- zigbee-cluster-library-specification
- JSBSim Reference Manual
- c++校园超市商品信息管理系统课程设计说明书(含源代码) (2).pdf
- 建筑供配电系统相关课件.pptx
- 企业管理规章制度及管理模式.doc
- vb打开摄像头.doc
- 云计算-可信计算中认证协议改进方案.pdf
- [详细完整版]单片机编程4.ppt
- c语言常用算法.pdf
- c++经典程序代码大全.pdf
- 单片机数字时钟资料.doc
- 11项目管理前沿1.0.pptx
- 基于ssm的“魅力”繁峙宣传网站的设计与实现论文.doc
- 智慧交通综合解决方案.pptx
- 建筑防潮设计-PowerPointPresentati.pptx
- SPC统计过程控制程序.pptx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功