github.com/TIBCOSoftware/flogo-lib@v0.5.9/core/data/resolve.go (about)

     1  package data
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/TIBCOSoftware/flogo-lib/logger"
     9  )
    10  
    11  type Resolver interface {
    12  	Resolve(toResolve string, scope Scope) (value interface{}, err error)
    13  }
    14  
    15  var resolver = &BasicResolver{}
    16  
    17  func GetBasicResolver() Resolver {
    18  	return resolver
    19  }
    20  
    21  type BasicResolver struct {
    22  }
    23  
    24  func (r *BasicResolver) Resolve(toResolve string, scope Scope) (value interface{}, err error) {
    25  
    26  	var details *ResolutionDetails
    27  
    28  	if strings.HasPrefix(toResolve, "${") {
    29  		details, err = GetResolutionDetailsOld(toResolve)
    30  	} else if strings.HasPrefix(toResolve, "$") {
    31  		details, err = GetResolutionDetails(toResolve[1:])
    32  	} else {
    33  
    34  		if scope == nil {
    35  			//todo is this what we should do in this circumstance? or throw an error?
    36  			return toResolve, nil
    37  		}
    38  
    39  		return SimpleScopeResolve(toResolve, scope)
    40  	}
    41  
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	if details == nil {
    47  		return nil, fmt.Errorf("unable to resolve '%s'", toResolve)
    48  	}
    49  
    50  	var exists bool
    51  
    52  	switch details.ResolverName {
    53  	case "property":
    54  		// Property resolution
    55  		provider := GetPropertyProvider()
    56  		value, exists = provider.GetProperty(details.Property + details.Path) //should we add the path and reset it to ""
    57  		if !exists {
    58  			err := fmt.Errorf("failed to resolve Property: '%s', ensure that property is configured in the application", details.Property)
    59  			logger.Error(err.Error())
    60  			return nil, err
    61  		}
    62  	case "env":
    63  		// Environment resolution
    64  		value, exists = os.LookupEnv(details.Property + details.Path)
    65  		if !exists {
    66  			err := fmt.Errorf("failed to resolve Environment Variable: '%s', ensure that variable is configured", details.Property)
    67  			logger.Error(err.Error())
    68  			return "", err
    69  		}
    70  	case ".":
    71  		//Current scope resolution
    72  		attr, exists := scope.GetAttr(details.Property)
    73  		if !exists {
    74  			return nil, fmt.Errorf("failed to resolve current scope: '%s', not found in scope", details.Property)
    75  		}
    76  		value = attr.Value()
    77  	default:
    78  		return nil, fmt.Errorf("unsupported resolver: %s", details.ResolverName)
    79  	}
    80  
    81  	value = GetComplexValue(value)
    82  	if details.Path != "" {
    83  		value, err = PathGetValue(value, details.Path)
    84  		if err != nil {
    85  			logger.Error(err.Error())
    86  			return nil, err
    87  		}
    88  	}
    89  
    90  	return value, nil
    91  }
    92  
    93  func SimpleScopeResolve(toResolve string, scope Scope) (value interface{}, err error) {
    94  	//idx := strings.Index(toResolve, ".")
    95  	idx := strings.IndexFunc(toResolve, isSep)
    96  
    97  	if idx != -1 {
    98  		attr, found := scope.GetAttr(toResolve[:idx])
    99  		if !found {
   100  			return nil, fmt.Errorf("could not resolve '%s'", toResolve)
   101  		}
   102  		value, err := PathGetValue(attr.Value(), toResolve[idx:])
   103  		if err != nil {
   104  			logger.Error(err.Error())
   105  			return nil, err
   106  		}
   107  		return value, nil
   108  
   109  	} else {
   110  		attr, found := scope.GetAttr(toResolve)
   111  		if !found {
   112  			return nil, fmt.Errorf("could not resolve '%s'", toResolve)
   113  		}
   114  
   115  		return attr.Value(), nil
   116  	}
   117  }
   118  
   119  type ResolutionDetails struct {
   120  	ResolverName string
   121  	Item         string
   122  	Property     string
   123  	Path         string
   124  }
   125  
   126  func GetResolutionDetails(toResolve string) (*ResolutionDetails, error) {
   127  
   128  	//todo optimize, maybe tokenize first
   129  	details := &ResolutionDetails{}
   130  
   131  	bracketIdx := strings.Index(toResolve, "]")
   132  	exprLen := len(toResolve)
   133  	if bracketIdx == (exprLen-1) && (strings.HasPrefix(toResolve, "property") || strings.HasPrefix(toResolve, "env")) {
   134  		//$property[] or $env[] resolution
   135  		itemIdx := strings.Index(toResolve, "[")
   136  		details.ResolverName = toResolve[:itemIdx]
   137  		details.Property = toResolve[itemIdx+1 : exprLen-1]
   138  		return details, nil
   139  	}
   140  
   141  	dotIdx := strings.Index(toResolve, ".")
   142  	if dotIdx == -1 {
   143  		return nil, fmt.Errorf("invalid resolution expression [%s]", toResolve)
   144  	}
   145  
   146  	itemIdx := strings.Index(toResolve[:dotIdx], "[")
   147  
   148  	if itemIdx != -1 {
   149  		details.Item = toResolve[itemIdx+1 : dotIdx-1]
   150  		details.ResolverName = toResolve[:itemIdx]
   151  	} else {
   152  		//For the case to get current scope attribute data
   153  		if strings.HasPrefix(toResolve, "$.") || strings.HasPrefix(toResolve, ".") {
   154  			details.ResolverName = toResolve[:dotIdx+1]
   155  		} else {
   156  			details.ResolverName = toResolve[:dotIdx]
   157  		}
   158  		//special case for activity without brackets
   159  		if strings.HasPrefix(toResolve, "activity") {
   160  			nextDot := strings.Index(toResolve[dotIdx+1:], ".") + dotIdx + 1
   161  			details.Item = toResolve[dotIdx+1 : nextDot]
   162  			dotIdx = nextDot
   163  		}
   164  	}
   165  
   166  	pathIdx := strings.IndexFunc(toResolve[dotIdx+1:], isSep)
   167  
   168  	if pathIdx != -1 {
   169  		pathStart := pathIdx + dotIdx + 1
   170  		details.Path = toResolve[pathStart:]
   171  		details.Property = toResolve[dotIdx+1 : pathStart]
   172  	} else {
   173  		details.Property = toResolve[dotIdx+1:]
   174  	}
   175  
   176  	return details, nil
   177  }
   178  
   179  func GetResolutionDetailsOld(toResolve string) (*ResolutionDetails, error) {
   180  
   181  	//todo optimize, maybe tokenize first
   182  
   183  	closeIdx := strings.Index(toResolve, "}")
   184  
   185  	if len(toResolve) < 4 || closeIdx == -1 {
   186  		return nil, fmt.Errorf("invalid resolution expression [%s]", toResolve)
   187  	}
   188  
   189  	details := &ResolutionDetails{}
   190  
   191  	dotIdx := strings.Index(toResolve, ".")
   192  
   193  	if dotIdx == -1 {
   194  		return nil, fmt.Errorf("invalid resolution expression [%s]", toResolve)
   195  	}
   196  
   197  	details.ResolverName = toResolve[2:dotIdx]
   198  
   199  	if details.ResolverName == "activity" {
   200  		nextDot := strings.Index(toResolve[dotIdx+1:], ".") + dotIdx + 1
   201  		details.Item = toResolve[dotIdx+1 : nextDot]
   202  		dotIdx = nextDot
   203  	}
   204  	details.Property = toResolve[dotIdx+1 : closeIdx]
   205  
   206  	if closeIdx+1 < len(toResolve) {
   207  		details.Path = toResolve[closeIdx+1:]
   208  	}
   209  
   210  	return details, nil
   211  }
   212  
   213  func isSep(r rune) bool {
   214  	return r == '.' || r == '['
   215  }
   216  
   217  func GetValueWithResolver(valueMap map[string]interface{}, key string) (interface{}, bool) {
   218  
   219  	val, exists := valueMap[key]
   220  
   221  	if !exists {
   222  		return nil, false
   223  	}
   224  
   225  	strVal, ok := val.(string)
   226  
   227  	if ok {
   228  		if strVal == "" {
   229  			return "", true
   230  		}
   231  
   232  		if strVal[0] == '$' {
   233  
   234  			v, err := GetBasicResolver().Resolve(strVal, nil)
   235  			if err != nil {
   236  				if strings.HasPrefix(err.Error(), "unsupported resolver") {
   237  					return val, true
   238  				}
   239  				//todo double check this case
   240  				return val, true
   241  			}
   242  
   243  			return v, true
   244  		} else {
   245  			return val, true
   246  		}
   247  	}
   248  
   249  	return val, true
   250  }
   251  
   252  func GetComplexValue(value interface{}) interface{} {
   253  	if value != nil {
   254  		switch t := value.(type) {
   255  		case *ComplexObject:
   256  			return t.Value
   257  		}
   258  	}
   259  	return value
   260  }