1
2
3
4
5
6 package dev.metaschema.core.metapath.format;
7
8 import org.apache.logging.log4j.LogManager;
9 import org.apache.logging.log4j.Logger;
10
11 import java.util.List;
12
13 import dev.metaschema.core.metapath.item.node.IAssemblyInstanceGroupedNodeItem;
14 import dev.metaschema.core.metapath.item.node.IAssemblyNodeItem;
15 import dev.metaschema.core.metapath.item.node.IDocumentNodeItem;
16 import dev.metaschema.core.metapath.item.node.IFieldNodeItem;
17 import dev.metaschema.core.metapath.item.node.IFlagNodeItem;
18 import dev.metaschema.core.metapath.item.node.IModelNodeItem;
19 import dev.metaschema.core.metapath.item.node.IModuleNodeItem;
20 import dev.metaschema.core.metapath.item.node.IRootAssemblyNodeItem;
21 import dev.metaschema.core.model.IFlagInstance;
22 import dev.metaschema.core.model.INamedModelInstance;
23 import dev.metaschema.core.model.JsonGroupAsBehavior;
24 import dev.metaschema.core.qname.IEnhancedQName;
25 import dev.metaschema.core.util.ObjectUtils;
26 import edu.umd.cs.findbugs.annotations.NonNull;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 public class JsonPointerFormatter implements IPathFormatter {
52 private static final Logger LOGGER = LogManager.getLogger(JsonPointerFormatter.class);
53
54 @Override
55 @NonNull
56 public String formatMetaschema(IModuleNodeItem metaschema) {
57
58 return "";
59 }
60
61 @Override
62 @NonNull
63 public String formatDocument(IDocumentNodeItem document) {
64
65 return "";
66 }
67
68 @Override
69 @NonNull
70 public String formatRootAssembly(IRootAssemblyNodeItem root) {
71 String jsonName = root.getDefinition().getJsonName();
72 return escapeJsonPointer(jsonName);
73 }
74
75 @Override
76 @NonNull
77 public String formatAssembly(IAssemblyNodeItem assembly) {
78 return formatModelItem(assembly);
79 }
80
81 @Override
82 @NonNull
83 public String formatAssembly(IAssemblyInstanceGroupedNodeItem assembly) {
84 return formatModelItem(assembly);
85 }
86
87 @Override
88 @NonNull
89 public String formatField(IFieldNodeItem field) {
90 return formatModelItem(field);
91 }
92
93 @Override
94 @NonNull
95 public String formatFlag(IFlagNodeItem flag) {
96
97 return escapeJsonPointer(flag.getQName().getLocalName());
98 }
99
100
101
102
103
104
105
106
107
108 @NonNull
109 private static String formatModelItem(@NonNull IModelNodeItem<?, ?> item) {
110 INamedModelInstance instance = item.getInstance();
111 if (instance == null) {
112
113 return escapeJsonPointer(item.getQName().getLocalName());
114 }
115
116 String jsonName = escapeJsonPointer(instance.getJsonName());
117 JsonGroupAsBehavior behavior = instance.getJsonGroupAsBehavior();
118
119 switch (behavior) {
120 case KEYED:
121 String keyValue = getJsonKeyValue(item, instance);
122 return jsonName + "/" + escapeJsonPointer(keyValue);
123 case LIST:
124
125 return jsonName + "/" + (item.getPosition() - 1);
126 case SINGLETON_OR_LIST:
127 int siblingCount = countSiblings(item);
128 if (siblingCount > 1) {
129
130 return jsonName + "/" + (item.getPosition() - 1);
131 }
132
133 return jsonName;
134 case NONE:
135 default:
136 return jsonName;
137 }
138 }
139
140
141
142
143
144
145
146
147
148
149 @NonNull
150 private static String getJsonKeyValue(
151 @NonNull IModelNodeItem<?, ?> item,
152 @NonNull INamedModelInstance instance) {
153 IFlagInstance keyFlag = instance.getEffectiveJsonKey();
154 if (keyFlag != null) {
155 IEnhancedQName keyFlagQName = keyFlag.getQName();
156 IFlagNodeItem flagItem = item.getFlagByName(keyFlagQName);
157 if (flagItem != null) {
158 return flagItem.toAtomicItem().asString();
159 }
160 }
161
162
163 if (LOGGER.isWarnEnabled()) {
164 LOGGER.warn("Unable to resolve JSON key for KEYED collection item '{}', falling back to numeric index",
165 item.getQName().getLocalName());
166 }
167 return ObjectUtils.notNull(String.valueOf(item.getPosition() - 1));
168 }
169
170
171
172
173
174
175
176
177 private static int countSiblings(@NonNull IModelNodeItem<?, ?> item) {
178 IAssemblyNodeItem parent = item.getParentContentNodeItem();
179 if (parent == null) {
180 return 1;
181 }
182 List<? extends IModelNodeItem<?, ?>> siblings = parent.getModelItemsByName(item.getQName());
183 return siblings.size();
184 }
185
186
187
188
189
190
191
192
193
194
195 @NonNull
196 private static String escapeJsonPointer(@NonNull String value) {
197
198 return ObjectUtils.notNull(value.replace("~", "~0").replace("/", "~1"));
199 }
200 }