1// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
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  			xeipuuv
16// author-github 	https://github.com/xeipuuv
17// author-mail		xeipuuv@gmail.com
18//
19// repository-name	gojsonreference
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 gojsonreference
27
28import (
29	"errors"
30	"net/url"
31	"path/filepath"
32	"runtime"
33	"strings"
34
35	"github.com/xeipuuv/gojsonpointer"
36)
37
38const (
39	const_fragment_char = `#`
40)
41
42func NewJsonReference(jsonReferenceString string) (JsonReference, error) {
43
44	var r JsonReference
45	err := r.parse(jsonReferenceString)
46	return r, err
47
48}
49
50type JsonReference struct {
51	referenceUrl     *url.URL
52	referencePointer gojsonpointer.JsonPointer
53
54	HasFullUrl      bool
55	HasUrlPathOnly  bool
56	HasFragmentOnly bool
57	HasFileScheme   bool
58	HasFullFilePath bool
59}
60
61func (r *JsonReference) GetUrl() *url.URL {
62	return r.referenceUrl
63}
64
65func (r *JsonReference) GetPointer() *gojsonpointer.JsonPointer {
66	return &r.referencePointer
67}
68
69func (r *JsonReference) String() string {
70
71	if r.referenceUrl != nil {
72		return r.referenceUrl.String()
73	}
74
75	if r.HasFragmentOnly {
76		return const_fragment_char + r.referencePointer.String()
77	}
78
79	return r.referencePointer.String()
80}
81
82func (r *JsonReference) IsCanonical() bool {
83	return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullUrl)
84}
85
86// "Constructor", parses the given string JSON reference
87func (r *JsonReference) parse(jsonReferenceString string) (err error) {
88
89	r.referenceUrl, err = url.Parse(jsonReferenceString)
90	if err != nil {
91		return
92	}
93	refUrl := r.referenceUrl
94
95	if refUrl.Scheme != "" && refUrl.Host != "" {
96		r.HasFullUrl = true
97	} else {
98		if refUrl.Path != "" {
99			r.HasUrlPathOnly = true
100		} else if refUrl.RawQuery == "" && refUrl.Fragment != "" {
101			r.HasFragmentOnly = true
102		}
103	}
104
105	r.HasFileScheme = refUrl.Scheme == "file"
106	if runtime.GOOS == "windows" {
107		// on Windows, a file URL may have an extra leading slash, and if it
108		// doesn't then its first component will be treated as the host by the
109		// Go runtime
110		if refUrl.Host == "" && strings.HasPrefix(refUrl.Path, "/") {
111			r.HasFullFilePath = filepath.IsAbs(refUrl.Path[1:])
112		} else {
113			r.HasFullFilePath = filepath.IsAbs(refUrl.Host + refUrl.Path)
114		}
115	} else {
116		r.HasFullFilePath = filepath.IsAbs(refUrl.Path)
117	}
118
119	// invalid json-pointer error means url has no json-pointer fragment. simply ignore error
120	r.referencePointer, _ = gojsonpointer.NewJsonPointer(refUrl.Fragment)
121
122	return
123}
124
125// Creates a new reference from a parent and a child
126// If the child cannot inherit from the parent, an error is returned
127func (r *JsonReference) Inherits(child JsonReference) (*JsonReference, error) {
128	if child.GetUrl() == nil {
129		return nil, errors.New("childUrl is nil!")
130	}
131
132	if r.GetUrl() == nil {
133		return nil, errors.New("parentUrl is nil!")
134	}
135
136	// Get a copy of the parent url to make sure we do not modify the original.
137	// URL reference resolving fails if the fragment of the child is empty, but the parent's is not.
138	// The fragment of the child must be used, so the fragment of the parent is manually removed.
139	parentUrl := *r.GetUrl()
140	parentUrl.Fragment = ""
141
142	ref, err := NewJsonReference(parentUrl.ResolveReference(child.GetUrl()).String())
143	if err != nil {
144		return nil, err
145	}
146	return &ref, err
147}
148