1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.google.api.generator.gapic.model; 16 17 import com.google.api.generator.engine.ast.ConcreteReference; 18 import com.google.api.generator.engine.ast.Reference; 19 import com.google.api.generator.engine.ast.TypeNode; 20 import com.google.api.generator.engine.ast.VaporReference; 21 import com.google.api.generator.gapic.utils.JavaStyle; 22 import com.google.api.generator.gapic.utils.ResourceNameConstants; 23 import com.google.api.pathtemplate.PathTemplate; 24 import com.google.auto.value.AutoValue; 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.collect.ImmutableMap; 27 import java.util.List; 28 import java.util.Objects; 29 import javax.annotation.Nullable; 30 31 @AutoValue 32 public abstract class ResourceName { 33 static final String SLASH = "/"; 34 static final Reference RESOURCE_NAME_REF = 35 ConcreteReference.withClazz(com.google.api.resourcenames.ResourceName.class); 36 37 // The original binding variable name. 38 // This should be in lower_snake_case in the proto, and expected to be surrounded by braces. 39 // Example: In projects/{project}/billingAccounts/billing_account, the variable name would be 40 // "billing_account." variableName()41 public abstract String variableName(); 42 43 // The Java package of the project, or that of a subpackage where the resource name was defined. 44 // That is, resource names defined outside of this project will still have the project's package. pakkage()45 public abstract String pakkage(); 46 47 // The resource type. resourceTypeString()48 public abstract String resourceTypeString(); 49 50 // A list of patterns such as projects/{project}/locations/{location}/resources/{this_resource}. 51 // Order is copuled to the method variants and ordering in the reosurce name helper, as well as 52 // the relevant client tests. patterns()53 public abstract ImmutableList<String> patterns(); 54 55 // The Java TypeNode of the resource name helper class to generate. type()56 public abstract TypeNode type(); 57 isOnlyWildcard()58 public abstract boolean isOnlyWildcard(); 59 60 @Nullable getMatchingPattern(HttpBindings bindings)61 public String getMatchingPattern(HttpBindings bindings) { 62 List<String> bindingPatterns = 63 ImmutableList.<String>builder() 64 .add(bindings.pattern()) 65 .addAll(bindings.additionalPatterns()) 66 .build(); 67 68 for (String bindingPattern : bindingPatterns) { 69 PathTemplate bindingTemplate = PathTemplate.create(bindingPattern); 70 for (String resNamePattern : patterns()) { 71 PathTemplate restNamePatternTemplate = PathTemplate.create(resNamePattern); 72 ImmutableMap.Builder<String, String> mb = ImmutableMap.builder(); 73 for (String var : restNamePatternTemplate.vars()) { 74 mb.put(var, var + (var.hashCode() % 100)); 75 } 76 77 String resNameValue = restNamePatternTemplate.instantiate(mb.build()); 78 mb = ImmutableMap.builder(); 79 for (String var : bindingTemplate.vars()) { 80 mb.put(var, resNameValue); 81 } 82 83 String url = bindingTemplate.instantiate(mb.build()); 84 if (bindingTemplate.matches(url)) { 85 return resNamePattern; 86 } 87 } 88 } 89 return null; 90 } 91 92 // The message in which this resource was defined. Optional. 93 // This is expected to be empty for file-level definitions. 94 @Nullable parentMessageName()95 public abstract String parentMessageName(); 96 97 @Nullable description()98 public abstract String description(); 99 hasParentMessageName()100 public boolean hasParentMessageName() { 101 return parentMessageName() != null; 102 } 103 hasDescription()104 public boolean hasDescription() { 105 return description() != null; 106 } 107 resourceTypeName()108 public String resourceTypeName() { 109 return resourceTypeString().substring(resourceTypeString().indexOf(SLASH) + 1); 110 } 111 builder()112 public static Builder builder() { 113 return new AutoValue_ResourceName.Builder().setIsOnlyWildcard(false); 114 } 115 createWildcard(String resourceTypeString, String pakkage)116 public static ResourceName createWildcard(String resourceTypeString, String pakkage) { 117 String placeholderVarName = 118 JavaStyle.toLowerCamelCase( 119 resourceTypeString.substring(resourceTypeString.indexOf(SLASH) + 1)); 120 return builder() 121 .setVariableName(placeholderVarName) 122 .setPakkage(pakkage) 123 .setResourceTypeString(resourceTypeString) 124 .setPatterns(ImmutableList.of(ResourceNameConstants.WILDCARD_PATTERN)) 125 .setIsOnlyWildcard(true) 126 .setType(TypeNode.withReference(RESOURCE_NAME_REF)) 127 .build(); 128 } 129 130 @Override equals(Object o)131 public boolean equals(Object o) { 132 if (!(o instanceof ResourceName)) { 133 return false; 134 } 135 136 ResourceName other = (ResourceName) o; 137 // Exclude the description from the resource name because it's just a comment. 138 return variableName().equals(other.variableName()) 139 && pakkage().equals(other.pakkage()) 140 && resourceTypeString().equals(other.resourceTypeString()) 141 && patterns().equals(other.patterns()) 142 && Objects.equals(parentMessageName(), other.parentMessageName()) 143 && Objects.equals(type(), other.type()); 144 } 145 146 @Override hashCode()147 public int hashCode() { 148 int parentMessageNameHashCode = 149 parentMessageName() == null ? 0 : parentMessageName().hashCode(); 150 int typeHashCode = type() == null ? 0 : type().hashCode(); 151 return 17 * variableName().hashCode() 152 + 19 * pakkage().hashCode() 153 + 23 * resourceTypeString().hashCode() 154 + 31 * patterns().hashCode() 155 + 37 * parentMessageNameHashCode 156 + 41 * typeHashCode; 157 } 158 159 @AutoValue.Builder 160 public abstract static class Builder { setVariableName(String variableName)161 public abstract Builder setVariableName(String variableName); 162 setPakkage(String pakkage)163 public abstract Builder setPakkage(String pakkage); 164 setResourceTypeString(String resourceTypeString)165 public abstract Builder setResourceTypeString(String resourceTypeString); 166 setPatterns(List<String> patterns)167 public abstract Builder setPatterns(List<String> patterns); 168 setParentMessageName(String parentMessageName)169 public abstract Builder setParentMessageName(String parentMessageName); 170 setDescription(String description)171 public abstract Builder setDescription(String description); 172 173 // Private setters. setType(TypeNode type)174 abstract Builder setType(TypeNode type); 175 setIsOnlyWildcard(boolean isOnlyWildcard)176 abstract Builder setIsOnlyWildcard(boolean isOnlyWildcard); 177 178 // Private accessors. pakkage()179 abstract String pakkage(); 180 resourceTypeString()181 abstract String resourceTypeString(); 182 isOnlyWildcard()183 abstract boolean isOnlyWildcard(); 184 185 // Private. autoBuild()186 abstract ResourceName autoBuild(); 187 build()188 public ResourceName build() { 189 if (!isOnlyWildcard()) { 190 String typeName = 191 resourceTypeString().substring(resourceTypeString().lastIndexOf(SLASH) + 1); 192 setType( 193 TypeNode.withReference( 194 VaporReference.builder() 195 .setName(String.format("%sName", typeName)) 196 .setPakkage(pakkage()) 197 .setSupertypeReference(RESOURCE_NAME_REF) 198 .build())); 199 } 200 return autoBuild(); 201 } 202 } 203 } 204