Skip to content

Commit

Permalink
Add Playground with files parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
tomkowz committed Jul 21, 2015
1 parent 6629288 commit e27e62d
Show file tree
Hide file tree
Showing 31 changed files with 610 additions and 85 deletions.
11 changes: 11 additions & 0 deletions ExpressionsLoader.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//: Playground - noun: a place where people can play

import UIKit

let expsBase = ExpressionsLoader.loadExpressions("base")
let expsPL = ExpressionsLoader.loadExpressions("pl")
let translations = TranslationsLoader.loadTranslations("base")

for translation in translations {
println(translation.key)
}
28 changes: 28 additions & 0 deletions ExpressionsLoader.playground/Resources/base.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"welcome": "welcome",

"cars": {
"one": "1 car",
"ie:x=2": "2 cars",
"more": "%d cars"
},

"forgot-password": {
"@100": "Forgot Password? Help.",
"@200": "Forgot Password? Get password Help.",
"@300": "Forgotten Your Password? Get password Help."
},

"car-sentence": {
"one": {
"@100": "one car",
"@200": "just one car",
"@300": "you've got just one car"
},

"more": {
"@100": "%d cars",
"@300": "you've got %d cars"
}
}
}
11 changes: 11 additions & 0 deletions ExpressionsLoader.playground/Resources/expressions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"base": {
"one": "ie:x=1",
"two": "ie:x=2",
"more": "exp:(^[^1])|(^\\d{2,})"
},

"pl": {
"few": "exp:(((?!1).[2-4]{1})$)|(^[2-4]$)"
}
}
8 changes: 8 additions & 0 deletions ExpressionsLoader.playground/Sources/CountryCode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation

/**
https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 (lowercase) + "base"
Represents country codes in framework.
Country codes are used to get correct user device's language.
*/
public typealias CountryCode = String
9 changes: 9 additions & 0 deletions ExpressionsLoader.playground/Sources/ExpressionType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation

/**
Represents expressions in the framework.
*/
public protocol ExpressionType {
/// Identifier of expression.
var identifier: String {get}
}
27 changes: 27 additions & 0 deletions ExpressionsLoader.playground/Sources/ExpressionsLoader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation

/**
Used to load content from `expressions.json` file for specified language.
*/
public final class ExpressionsLoader: JSONFileLoader {

/**
Loads expressions for specified language.

:param: countryCode A country code

:returns: array of loaded expressions.
*/
public class func loadExpressions(countryCode: CountryCode) -> [ProcessableExpression] {
var expressions = [ProcessableExpression]()
if let json = self.load("expressions", fileType: "json", bundle: NSBundle.mainBundle()),
let expressionsDict = json[countryCode] as? Dictionary<String, String> {
for (identifier, pattern) in expressionsDict {
expressions.append(ProcessableExpression(identifier: identifier, pattern: pattern))
}
} else {
println("expressions.json file structure is incorrect.")
}
return expressions
}
}
11 changes: 11 additions & 0 deletions ExpressionsLoader.playground/Sources/ExpressionsPatternType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

/**
Represents objects that have expression pattern.
Example an expression that contains just pattern, without things like length
variations.
*/
public protocol ExpressionPatternType {
/// A pattern.
var pattern: String {get}
}
45 changes: 45 additions & 0 deletions ExpressionsLoader.playground/Sources/JSONFileLoader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Foundation

/**
Simple JSON loader.
*/
public class JSONFileLoader {
public typealias JSONDictionary = Dictionary<String, AnyObject>

/**
Load content of file with specified name, type and bundle.

:param: fileName A name of a file.
:param: fileType A type of a file.
:param: bundle A bundle when file is located.

:return: JSON or nil.
*/
public final class func load(fileName: String, fileType: String, bundle: NSBundle = NSBundle.mainBundle()) -> JSONDictionary? {
if let fileURL = bundle.URLForResource(fileName, withExtension: fileType) {
return load(fileURL)
}
println("Cannot find file \(fileName).\(fileType).")
return nil
}

/**
Loads file for specified URL and try to serialize it.

:params: fileURL url to JSON file.

:return: Dictionary with content of JSON file or nil.
*/
private class func load(fileURL: NSURL) -> JSONDictionary? {
if let data = NSData(contentsOfURL: fileURL) {
var error: NSError?
if let dictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? JSONDictionary {
return dictionary
} else {
print("Cannot parse JSON. It might be broken.")
}
}
print("Cannot load content of file.")
return nil
}
}
18 changes: 18 additions & 0 deletions ExpressionsLoader.playground/Sources/LengthVariation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

/**
Struct that represents length variation.
*/
public struct LengthVariation: LengthVariationType {
/// Length - width of a screen.
public let length: Int

/// localized string.
public let value: String

/// Create length variation object.
public init(length: Int, value: String) {
self.length = length
self.value = value
}
}
10 changes: 10 additions & 0 deletions ExpressionsLoader.playground/Sources/LengthVariationType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Foundation

/// Represents length variation.
public protocol LengthVariationType {
/// Length of a screen.
var length: Int {get}

/// Localized value.
var value: String {get}
}
12 changes: 12 additions & 0 deletions ExpressionsLoader.playground/Sources/Processable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation

/**
Specifies objects that need processing and are not final objects that can be
used in the framework. An example of such object might be `ProcessableExpression`
that is not ready to be used because it has to be converted into normal kind of
expression that has its fields correctly filled.

Another good example is `TranslationType` object that contains expression or few
that need to be processed.
*/
protocol Processable {}
18 changes: 18 additions & 0 deletions ExpressionsLoader.playground/Sources/ProcessableExpression.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

/**
Represents loaded expression that will be processed later.
*/
public struct ProcessableExpression: ExpressionType, ExpressionPatternType, Processable {
/// Identifier of expression.
public let identifier: String

/// Pattern of expression.
public let pattern: String

/// Creates expression.
public init(identifier: String, pattern: String) {
self.identifier = identifier
self.pattern = pattern
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

/**
Represents processable expression with length variations.
*/
public struct ProcessableLengthVariationExpression: ExpressionType, Processable {
/// Identifier of expression.
public var identifier: String

/// Array of length variations
var variations: [LengthVariation]

/// Creates instance of the class.
public init(identifier: String, variations: [LengthVariation]) {
self.identifier = identifier
self.variations = variations
}
}
18 changes: 18 additions & 0 deletions ExpressionsLoader.playground/Sources/ProcessableTranslation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

/**
Represents translation which contains expressions that are not processed yet.
*/
public struct ProcessableTranslation: TranslationType, Processable {
/// Key that identifies translation.
public let key: String

/// Array with loaded expressions.
let loadedExpressions: [ProcessableExpression]

/// Creates instances of the class.
public init(key: String, loadedExpressions: [ProcessableExpression]) {
self.key = key
self.loadedExpressions = loadedExpressions
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

/**
Represents translation which contains expressions with length variations.
*/
public struct ProcessableTranslationLengthVariationExpression: TranslationType, Processable {
/// Key that identifies translation.
public let key: String

/// Array with expressions.
let expressions: [ProcessableLengthVariationExpression]

/// Creates instances of the class.
public init(key: String, expressions: [ProcessableLengthVariationExpression]) {
self.key = key
self.expressions = expressions
}
}
106 changes: 106 additions & 0 deletions ExpressionsLoader.playground/Sources/Regex.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// Regex.swift
// Swifternalization
//
// Created by Tomasz Szulc on 27/06/15.
// Copyright (c) 2015 Tomasz Szulc. All rights reserved.
//

import Foundation

/**
Class uses NSRegularExpression internally and simplifies its usability.
*/
public class Regex {

/**
Return match in a string. Optionally with index of capturing group

:param: str A string that will be matched.
:param: pattern A regex pattern.

:returns: `String` that matches pattern or nil.
*/
public class func matchInString(str: String, pattern: String, capturingGroupIdx: Int?) -> String? {
var resultString: String?

let range = NSMakeRange(0, count(str))
regexp(pattern)?.enumerateMatchesInString(str, options: nil, range: range, usingBlock: { result, flags, stop in
if let result = result {
if let capturingGroupIdx = capturingGroupIdx where result.numberOfRanges > capturingGroupIdx {
resultString = self.substring(str, range: result.rangeAtIndex(capturingGroupIdx))
} else {
resultString = self.substring(str, range: result.range)
}
}
})

return resultString
}


/**
Return first match in a string.

:param: str A string that will be matched.
:param: pattern A regexp pattern.

:returns: `String` that matches pattern or nil.
*/
class func firstMatchInString(str: String, pattern: String) -> String? {
if let result = regexp(pattern)?.firstMatchInString(str, options: .ReportCompletion, range: NSMakeRange(0, count(str))) {
return substring(str, range: result.range)
}
return nil
}

/**
Return all matches in a string.

:param: str A string that will be matched.
:param: pattern A regexp pattern.

:returns: Array of `Strings`s. If nothing found empty array is returned.
*/
class func matchesInString(str: String, pattern: String) -> [String] {
var matches = [String]()
if let results = regexp(pattern)?.matchesInString(str, options: .ReportCompletion, range: NSMakeRange(0, count(str))) as? [NSTextCheckingResult] {
for result in results {
matches.append(substring(str, range: result.range))
}
}

return matches
}

/**
Returns new `NSRegularExpression` object.

:param: pattern A regexp pattern.

:returns: `NSRegularExpression` object or nil if it cannot be created.
*/
private class func regexp(pattern: String) -> NSRegularExpression? {
var error: NSError? = nil
var regexp = NSRegularExpression(pattern: pattern, options: .CaseInsensitive, error: &error)
if error != nil {
println(error!)
}
return regexp
}

/**
Method that substring string with passed range.

:param: str A string that is source of substraction.
:param: range A range that tells which part of `str` will be substracted.

:returns: A string contained in `range`.
*/
private class func substring(str: String, range: NSRange) -> String {
let startRange = advance(str.startIndex, range.location)
let endRange = advance(startRange, range.length)

return str.substringWithRange(Range(start: startRange, end: endRange))
}
}
Loading

0 comments on commit e27e62d

Please sign in to comment.