4141import java .util .ArrayList ;
4242import java .util .Arrays ;
4343import java .util .Collection ;
44+ import java .util .Collections ;
4445import java .util .HashSet ;
4546import java .util .LinkedHashMap ;
4647import java .util .List ;
5051import java .util .Set ;
5152import java .util .function .Consumer ;
5253import java .util .function .Supplier ;
54+ import java .util .stream .Collectors ;
5355import software .amazon .smithy .model .SourceLocation ;
5456import software .amazon .smithy .model .node .ArrayNode ;
5557import software .amazon .smithy .model .node .BooleanNode ;
9698
9799final class IdlModelLoader {
98100
101+ private static final String PUT_KEY = "put" ;
102+ private static final String CREATE_KEY = "create" ;
103+ private static final String READ_KEY = "read" ;
104+ private static final String UPDATE_KEY = "update" ;
105+ private static final String DELETE_KEY = "delete" ;
106+ private static final String LIST_KEY = "list" ;
107+ private static final String RESOURCES_KEY = "resources" ;
108+ private static final String OPERATIONS_KEY = "operations" ;
109+ private static final String COLLECTION_OPERATIONS_KEY = "collectionOperations" ;
110+ private static final String IDENTIFIERS_KEY = "identifiers" ;
111+ private static final String VERSION_KEY = "version" ;
112+ private static final String TYPE_KEY = "type" ;
113+
114+ static final Collection <String > RESOURCE_PROPERTY_NAMES = ListUtils .of (
115+ TYPE_KEY , CREATE_KEY , READ_KEY , UPDATE_KEY , DELETE_KEY , LIST_KEY ,
116+ IDENTIFIERS_KEY , RESOURCES_KEY , OPERATIONS_KEY , PUT_KEY , COLLECTION_OPERATIONS_KEY );
117+ static final List <String > SERVICE_PROPERTY_NAMES = ListUtils .of (
118+ TYPE_KEY , VERSION_KEY , OPERATIONS_KEY , RESOURCES_KEY );
119+
99120 // Top-level statements
100121 private static final Set <String > STATEMENTS = SetUtils .of (
101122 "namespace" , "use" , "service" , "operation" , "resource" , "structure" , "union" ,
@@ -119,7 +140,6 @@ final class IdlModelLoader {
119140 private final SmithyModelLexer lexer ;
120141 private final LoaderVisitor visitor ;
121142 private final List <Pair <String , Node >> pendingTraits = new ArrayList <>();
122- private final Set <VersionFeature > features = new HashSet <>();
123143
124144 private Token current ;
125145 private DocComment pendingDocComment ;
@@ -148,64 +168,6 @@ private IdlModelLoader(String filename, SmithyModelLexer lexer, LoaderVisitor vi
148168 }
149169 }
150170
151- enum VersionFeature {
152- ALLOW_USE_BEFORE_NAMESPACE {
153- @ Override
154- void validate (IdlModelLoader loader ) {
155- if (loader .namespace == null && !loader .features .contains (this )) {
156- raise (loader , "Use statements must appear after a namespace is defined" );
157- }
158- }
159- },
160-
161- ALLOW_MULTIPLE_NAMESPACES {
162- @ Override
163- void validate (IdlModelLoader loader ) {
164- if (loader .namespace != null && !loader .features .contains (this )) {
165- raise (loader , format (
166- "Only a single namespace can be declared per/file. The previous namespace was "
167- + "set to `%s`." , loader .namespace ));
168- }
169- }
170- },
171-
172- ALLOW_METADATA_AFTER_NAMESPACE {
173- @ Override
174- void validate (IdlModelLoader loader ) {
175- if (loader .namespace != null && !loader .features .contains (this )) {
176- raise (loader , "Metadata statements must appear before a namespace statement" );
177- }
178- }
179- },
180-
181- ALLOW_MULTIPLE_VERSIONS {
182- @ Override
183- void validate (IdlModelLoader loader ) {
184- if (loader .definedVersion != null && !loader .features .contains (this )) {
185- raise (loader , "Cannot define multiple versions in the same file" );
186- }
187- }
188- };
189-
190- abstract void validate (IdlModelLoader loader );
191-
192- // Halts parsing if a version has been explicitly set, otherwise
193- // adds a WARNING event.
194- void raise (IdlModelLoader loader , String message ) {
195- if (loader .definedVersion != null ) {
196- throw loader .syntax (message );
197- }
198-
199- loader .visitor .onError (ValidationEvent .builder ()
200- .eventId (Validator .MODEL_ERROR )
201- .severity (Severity .WARNING )
202- .sourceLocation (loader .current )
203- .message ("Detected deprecated IDL features that will break in future versions "
204- + "of Smithy: " + message )
205- .build ());
206- }
207- }
208-
209171 private enum TraitValueType { SHAPE , MEMBER , APPLY }
210172
211173 /**
@@ -312,7 +274,11 @@ private void parseStatement(Token token) {
312274 }
313275
314276 private void parseNamespace () {
315- VersionFeature .ALLOW_MULTIPLE_NAMESPACES .validate (this );
277+ if (namespace != null ) {
278+ throw syntax ("Only a single namespace can be declared per/file. The previous namespace was set to "
279+ + "`" + namespace + "`." );
280+ }
281+
316282 String parsedNamespace = expect (UNQUOTED ).lexeme ;
317283
318284 if (!ShapeId .isValidNamespace (parsedNamespace )) {
@@ -324,7 +290,9 @@ private void parseNamespace() {
324290 }
325291
326292 private void parseUseStatement () {
327- VersionFeature .ALLOW_USE_BEFORE_NAMESPACE .validate (this );
293+ if (namespace == null ) {
294+ throw syntax ("Use statements must appear after a namespace is defined" );
295+ }
328296
329297 if (definedShapes ) {
330298 throw syntax ("A use statement must come before any shape definition" );
@@ -367,25 +335,23 @@ private void onVersion(Node value) {
367335 }
368336
369337 String parsedVersion = value .expectStringNode ().getValue ();
370- VersionFeature .ALLOW_MULTIPLE_VERSIONS .validate (this );
338+
339+ if (definedVersion != null ) {
340+ throw syntax ("Cannot define multiple versions in the same file" );
341+ }
371342
372343 if (!SmithyVersion .isSupported (parsedVersion )) {
373344 throw syntax (format ("Invalid Smithy version number: %s" , parsedVersion ));
374345 }
375346
376347 definedVersion = parsedVersion ;
377-
378- // Enable old Smithy 0.4.0 features that were removed in 0.5.0.
379- if (definedVersion .equals (SmithyVersion .VERSION_0_4_0 .value )) {
380- features .add (VersionFeature .ALLOW_USE_BEFORE_NAMESPACE );
381- features .add (VersionFeature .ALLOW_MULTIPLE_NAMESPACES );
382- features .add (VersionFeature .ALLOW_METADATA_AFTER_NAMESPACE );
383- features .add (VersionFeature .ALLOW_MULTIPLE_VERSIONS );
384- }
385348 }
386349
387350 private void parseMetadata () {
388- VersionFeature .ALLOW_METADATA_AFTER_NAMESPACE .validate (this );
351+ if (namespace != null ) {
352+ throw syntax ("Metadata statements must appear before a namespace statement" );
353+ }
354+
389355 definedMetadata = true ;
390356
391357 // metadata key = value\n
@@ -822,20 +788,55 @@ private void parseService() {
822788 .source (sourceLocation );
823789
824790 ObjectNode shapeNode = parseObjectNode (expect (LBRACE ).getSourceLocation (), RBRACE );
825- shapeNode .warnIfAdditionalProperties (LoaderUtils .SERVICE_PROPERTY_NAMES );
826- LoaderUtils .loadServiceObject (builder , shapeId , shapeNode );
791+ shapeNode .warnIfAdditionalProperties (SERVICE_PROPERTY_NAMES );
792+ builder .version (shapeNode .expectStringMember (VERSION_KEY ).getValue ());
793+ optionalIdList (shapeNode , shapeId .getNamespace (), OPERATIONS_KEY ).forEach (builder ::addOperation );
794+ optionalIdList (shapeNode , shapeId .getNamespace (), RESOURCES_KEY ).forEach (builder ::addResource );
827795 visitor .onShape (builder );
828796 expectNewline ();
829797 }
830798
799+ static Optional <ShapeId > optionalId (ObjectNode node , String namespace , String name ) {
800+ return node .getStringMember (name ).map (stringNode -> stringNode .expectShapeId (namespace ));
801+ }
802+
803+ static List <ShapeId > optionalIdList (ObjectNode node , String namespace , String name ) {
804+ return node .getArrayMember (name )
805+ .map (array -> array .getElements ().stream ()
806+ .map (Node ::expectStringNode )
807+ .map (s -> s .expectShapeId (namespace ))
808+ .collect (Collectors .toList ()))
809+ .orElseGet (Collections ::emptyList );
810+ }
811+
831812 private void parseResource () {
832813 SourceLocation sourceLocation = currentLocation ();
833814 ShapeId shapeId = parseShapeName ();
834815 ResourceShape .Builder builder = ResourceShape .builder ().id (shapeId ).source (sourceLocation );
835816 visitor .onShape (builder );
836817 ObjectNode shapeNode = parseObjectNode (expect (LBRACE ).getSourceLocation (), RBRACE );
837- shapeNode .warnIfAdditionalProperties (LoaderUtils .RESOURCE_PROPERTY_NAMES );
838- LoaderUtils .loadResourceObject (builder , shapeId , shapeNode , visitor );
818+
819+ shapeNode .warnIfAdditionalProperties (RESOURCE_PROPERTY_NAMES );
820+ optionalId (shapeNode , shapeId .getNamespace (), PUT_KEY ).ifPresent (builder ::put );
821+ optionalId (shapeNode , shapeId .getNamespace (), CREATE_KEY ).ifPresent (builder ::create );
822+ optionalId (shapeNode , shapeId .getNamespace (), READ_KEY ).ifPresent (builder ::read );
823+ optionalId (shapeNode , shapeId .getNamespace (), UPDATE_KEY ).ifPresent (builder ::update );
824+ optionalId (shapeNode , shapeId .getNamespace (), DELETE_KEY ).ifPresent (builder ::delete );
825+ optionalId (shapeNode , shapeId .getNamespace (), LIST_KEY ).ifPresent (builder ::list );
826+ optionalIdList (shapeNode , shapeId .getNamespace (), OPERATIONS_KEY ).forEach (builder ::addOperation );
827+ optionalIdList (shapeNode , shapeId .getNamespace (), RESOURCES_KEY ).forEach (builder ::addResource );
828+ optionalIdList (shapeNode , shapeId .getNamespace (), COLLECTION_OPERATIONS_KEY )
829+ .forEach (builder ::addCollectionOperation );
830+
831+ // Load identifiers and resolve forward references.
832+ shapeNode .getObjectMember (IDENTIFIERS_KEY ).ifPresent (ids -> {
833+ for (Map .Entry <StringNode , Node > entry : ids .getMembers ().entrySet ()) {
834+ String name = entry .getKey ().getValue ();
835+ StringNode target = entry .getValue ().expectStringNode ();
836+ visitor .onShapeTarget (target .getValue (), target , id -> builder .addIdentifier (name , id ));
837+ }
838+ });
839+
839840 expectNewline ();
840841 }
841842
0 commit comments