1// Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) 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// author sigu-399 16// author-github https://github.com/sigu-399 17// author-mail sigu.399@gmail.com 18// 19// repository-name jsonreference 20// repository-desc An implementation of JSON Reference - Go language 21// 22// description Main and unique file. 23// 24// created 26-02-2013 25 26package jsonreference 27 28import ( 29 "errors" 30 "net/url" 31 "strings" 32 33 "github.com/PuerkitoBio/purell" 34 "github.com/go-openapi/jsonpointer" 35) 36 37const ( 38 fragmentRune = `#` 39) 40 41// New creates a new reference for the given string 42func New(jsonReferenceString string) (Ref, error) { 43 44 var r Ref 45 err := r.parse(jsonReferenceString) 46 return r, err 47 48} 49 50// MustCreateRef parses the ref string and panics when it's invalid. 51// Use the New method for a version that returns an error 52func MustCreateRef(ref string) Ref { 53 r, err := New(ref) 54 if err != nil { 55 panic(err) 56 } 57 return r 58} 59 60// Ref represents a json reference object 61type Ref struct { 62 referenceURL *url.URL 63 referencePointer jsonpointer.Pointer 64 65 HasFullURL bool 66 HasURLPathOnly bool 67 HasFragmentOnly bool 68 HasFileScheme bool 69 HasFullFilePath bool 70} 71 72// GetURL gets the URL for this reference 73func (r *Ref) GetURL() *url.URL { 74 return r.referenceURL 75} 76 77// GetPointer gets the json pointer for this reference 78func (r *Ref) GetPointer() *jsonpointer.Pointer { 79 return &r.referencePointer 80} 81 82// String returns the best version of the url for this reference 83func (r *Ref) String() string { 84 85 if r.referenceURL != nil { 86 return r.referenceURL.String() 87 } 88 89 if r.HasFragmentOnly { 90 return fragmentRune + r.referencePointer.String() 91 } 92 93 return r.referencePointer.String() 94} 95 96// IsRoot returns true if this reference is a root document 97func (r *Ref) IsRoot() bool { 98 return r.referenceURL != nil && 99 !r.IsCanonical() && 100 !r.HasURLPathOnly && 101 r.referenceURL.Fragment == "" 102} 103 104// IsCanonical returns true when this pointer starts with http(s):// or file:// 105func (r *Ref) IsCanonical() bool { 106 return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullURL) 107} 108 109// "Constructor", parses the given string JSON reference 110func (r *Ref) parse(jsonReferenceString string) error { 111 112 parsed, err := url.Parse(jsonReferenceString) 113 if err != nil { 114 return err 115 } 116 117 r.referenceURL, _ = url.Parse(purell.NormalizeURL(parsed, purell.FlagsSafe|purell.FlagRemoveDuplicateSlashes)) 118 refURL := r.referenceURL 119 120 if refURL.Scheme != "" && refURL.Host != "" { 121 r.HasFullURL = true 122 } else { 123 if refURL.Path != "" { 124 r.HasURLPathOnly = true 125 } else if refURL.RawQuery == "" && refURL.Fragment != "" { 126 r.HasFragmentOnly = true 127 } 128 } 129 130 r.HasFileScheme = refURL.Scheme == "file" 131 r.HasFullFilePath = strings.HasPrefix(refURL.Path, "/") 132 133 // invalid json-pointer error means url has no json-pointer fragment. simply ignore error 134 r.referencePointer, _ = jsonpointer.New(refURL.Fragment) 135 136 return nil 137} 138 139// Inherits creates a new reference from a parent and a child 140// If the child cannot inherit from the parent, an error is returned 141func (r *Ref) Inherits(child Ref) (*Ref, error) { 142 childURL := child.GetURL() 143 parentURL := r.GetURL() 144 if childURL == nil { 145 return nil, errors.New("child url is nil") 146 } 147 if parentURL == nil { 148 return &child, nil 149 } 150 151 ref, err := New(parentURL.ResolveReference(childURL).String()) 152 if err != nil { 153 return nil, err 154 } 155 return &ref, nil 156} 157