Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.swagger.v3.parser.processors;


import java.net.URI;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -1007,32 +1006,7 @@ private void processRefLink(Link subRef, String externalFile) {

// visible for testing
public static String join(String source, String fragment) {
try {
boolean isRelative = false;
if(source.startsWith("/") || source.startsWith(".")) {
isRelative = true;
}
URI uri = new URI(source);

if(!source.endsWith("/") && (fragment.startsWith("./") && "".equals(uri.getPath()))) {
uri = new URI(source + "/");
}
else if("".equals(uri.getPath()) && !fragment.startsWith("/")) {
uri = new URI(source + "/");
}
URI f = new URI(fragment);

URI resolved = uri.resolve(f);

URI normalized = resolved.normalize();
if(Character.isAlphabetic(normalized.toString().charAt(0)) && isRelative) {
return "./" + normalized.toString();
}
return normalized.toString();
}
catch(Exception e) {
return source;
}
return ReferencePathUtils.resolve(source, fragment);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ protected void updateRefs(ApiResponse response, String pathRef) {

protected void updateRefs(Example example, String pathRef) {
if(example.get$ref() != null) {
example.set$ref(computeRef(example.get$ref(), pathRef));
example.set$ref(computePreprocessedRef(example.get$ref(), pathRef));
}
}

Expand Down Expand Up @@ -239,7 +239,7 @@ protected void updateRefs(RequestBody body, String pathRef) {

protected void updateRefs(Schema model, String pathRef) {
if(model.get$ref() != null) {
model.set$ref(computeRef(model.get$ref(), pathRef));
model.set$ref(computePreprocessedRef(model.get$ref(), pathRef));
}
else if(model.getProperties() != null) {
// process properties
Expand Down Expand Up @@ -297,42 +297,37 @@ else if(model instanceof ArraySchema) {


protected boolean isLocalRef(String ref) {
if(ref.startsWith("#")) {
return true;
}
return false;
return ref.startsWith("#");
}

protected boolean isAbsoluteRef(String ref) {
if(!ref.startsWith(".")) {
return true;
}
return false;
return ReferencePathUtils.isAbsolute(ref);
}

private boolean isInternalSchemaRef(String $ref) {
if($ref.startsWith("#/components/schemas")) {
return true;
}
return false;
return $ref.startsWith("#/components/schemas");
}

protected String computeRef(String ref, String prefix) {
if(isLocalRef(ref)&& !isInternalSchemaRef(ref)) return computeLocalRef(ref, prefix);
if (ref.isEmpty()) return ref;
if(isAbsoluteRef(ref)) return ref;
if(isInternalSchemaRef(ref)) return ref;
return computeRelativeRef(ref, prefix);
}

protected String computeRelativeRef(String ref, String prefix) {
if(ref.startsWith("./")) {
return ReferencePathUtils.resolve(prefix, ref);
}

private String computePreprocessedRef(String ref, String prefix) {
if (isLocalRef(ref) && !isInternalSchemaRef(ref)) {
return computeLocalRef(ref, prefix);
}
if (!ref.startsWith(".") || ref.startsWith("./") || isInternalSchemaRef(ref)) {
return ref;
}
int iIdxOfSlash = prefix.lastIndexOf('/');
if(iIdxOfSlash != -1) {
return prefix.substring(0, iIdxOfSlash+1) + ref;
}
return prefix + ref;
return ReferencePathUtils.resolve(prefix, ref);
}

protected String computeLocalRef(String ref, String prefix) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package io.swagger.v3.parser.processors;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayDeque;
import java.util.Deque;

final class ReferencePathUtils {

private ReferencePathUtils() {
}

static boolean isAbsolute(String ref) {
if (ref == null) {
return false;
}
if (isAbsoluteFilePath(ref)) {
return true;
}
try {
return new URI(ref).isAbsolute();
} catch (URISyntaxException e) {
return hasScheme(ref);
}
}

static String resolve(String source, String ref) {
if (source == null || ref == null) {
return source;
}
try {
URI sourceUri = new URI(source);
if (!source.endsWith("/") && ref.startsWith("./") && "".equals(sourceUri.getPath())) {
sourceUri = new URI(source + "/");
} else if ("".equals(sourceUri.getPath()) && !ref.startsWith("/")) {
sourceUri = new URI(source + "/");
}

URI resolved = sourceUri.resolve(new URI(ref)).normalize();
String resolvedRef = resolved.toString();
if (source.startsWith("./") && !resolved.isAbsolute() &&
!resolvedRef.startsWith(".") && !resolvedRef.startsWith("/")) {
return "./" + resolvedRef;
}
return resolvedRef;
} catch (URISyntaxException | IllegalArgumentException e) {
return resolveRawPath(source, ref);
}
}

private static String resolveRawPath(String source, String ref) {
if (ref.isEmpty()) {
return stripFragment(source);
}
if (isAbsolute(ref)) {
return ref;
}
if (ref.startsWith("#")) {
return stripFragment(source) + ref;
}
if (ref.startsWith("?")) {
return stripQueryAndFragment(source) + ref;
}

String sourceFile = stripQueryAndFragment(source);
int lastSeparator = Math.max(sourceFile.lastIndexOf('/'), sourceFile.lastIndexOf('\\'));
if (lastSeparator == -1) {
return normalizeRelativePath(ref);
}

String resolved = sourceFile.substring(0, lastSeparator + 1) + ref;
if (hasScheme(sourceFile)) {
return resolved;
}
return normalizeRelativePath(resolved);
}

private static String normalizeRelativePath(String ref) {
int suffixStart = suffixStart(ref);
String path = suffixStart == -1 ? ref : ref.substring(0, suffixStart);
String suffix = suffixStart == -1 ? "" : ref.substring(suffixStart);
boolean leadingDotSlash = path.startsWith("./");
boolean leadingSlash = path.startsWith("/");
Deque<String> segments = new ArrayDeque<>();

for (String segment : path.split("/")) {
if (segment.isEmpty() || ".".equals(segment)) {
continue;
}
if ("..".equals(segment)) {
if (!segments.isEmpty() && !"..".equals(segments.peekLast())) {
segments.removeLast();
} else if (!leadingSlash) {
segments.addLast(segment);
}
} else {
segments.addLast(segment);
}
}

String normalized = String.join("/", segments);
if (leadingSlash) {
normalized = "/" + normalized;
} else if (leadingDotSlash && !normalized.startsWith("..") && !normalized.isEmpty()) {
normalized = "./" + normalized;
}
return normalized + suffix;
}

private static boolean isAbsoluteFilePath(String ref) {
return ref.startsWith("/") || ref.startsWith("\\") ||
(ref.length() >= 3 && Character.isLetter(ref.charAt(0)) && ref.charAt(1) == ':' &&
(ref.charAt(2) == '/' || ref.charAt(2) == '\\'));
}

private static boolean hasScheme(String ref) {
int colon = ref.indexOf(':');
if (colon <= 0 || !Character.isLetter(ref.charAt(0))) {
return false;
}
for (int i = 1; i < colon; i++) {
char character = ref.charAt(i);
if (!Character.isLetterOrDigit(character) && character != '+' && character != '-' && character != '.') {
return false;
}
}
return true;
}

private static String stripFragment(String value) {
int fragment = value.indexOf('#');
return fragment == -1 ? value : value.substring(0, fragment);
}

private static String stripQueryAndFragment(String value) {
int suffixStart = suffixStart(value);
return suffixStart == -1 ? value : value.substring(0, suffixStart);
}

private static int suffixStart(String value) {
int query = value.indexOf('?');
int fragment = value.indexOf('#');
if (query == -1) {
return fragment;
}
if (fragment == -1) {
return query;
}
return Math.min(query, fragment);
}
}
Loading
Loading