Sequence Update
This commit is contained in:
parent
a8e7988c2a
commit
a383c5da49
|
@ -23,9 +23,9 @@
|
|||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="10.363534"
|
||||
inkscape:cx="-6.368484"
|
||||
inkscape:cy="-0.048246091"
|
||||
inkscape:zoom="20.727068"
|
||||
inkscape:cx="4.1491638"
|
||||
inkscape:cy="6.8750679"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
|
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
@ -0,0 +1,157 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="RJActionSequence.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="20.727068"
|
||||
inkscape:cx="4.1491638"
|
||||
inkscape:cy="6.8750679"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g560" />
|
||||
<g
|
||||
id="g560"
|
||||
transform="translate(0.08802232,0.240022)">
|
||||
<g
|
||||
id="g24518"
|
||||
transform="matrix(1.0193268,0,0,1.0193268,0.13313592,0.48032339)">
|
||||
<g
|
||||
id="g24489"
|
||||
transform="matrix(0.5277136,0,0,0.5277136,-2.2543173,-4.6044791)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#f7b200;fill-opacity:1;stroke:none;stroke-width:2.56963;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||
id="path24485"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="-8.2018356"
|
||||
sodipodi:cy="-2.1710742"
|
||||
sodipodi:r1="7.9176526"
|
||||
sodipodi:r2="4.5684857"
|
||||
sodipodi:arg1="-1.5707963"
|
||||
sodipodi:arg2="-0.94247777"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -8.2018354,-10.088727 2.6852884,4.2216703 4.84484654,1.2492935 -3.18524704,3.85842889 0.3089911,4.99377581 -4.6538794,-1.8370299 -4.6538792,1.8370296 0.308991,-4.99377573 -3.185247,-3.85842907 4.844847,-1.2492932 z"
|
||||
inkscape:transform-center-y="0.31212486"
|
||||
transform="matrix(-0.41282678,0,0,-0.41282678,15.438508,17.099887)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:none;fill-opacity:1;stroke:#f7b200;stroke-width:0.56037;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill"
|
||||
id="path24487"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="-8.2018356"
|
||||
sodipodi:cy="-2.1710742"
|
||||
sodipodi:r1="7.9176526"
|
||||
sodipodi:r2="4.7268386"
|
||||
sodipodi:arg1="-1.5707963"
|
||||
sodipodi:arg2="-0.9424778"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -8.2018354,-10.088727 2.7783658,4.0935601 4.75176914,1.3774037 -3.03464454,3.90736248 0.1583886,4.94484222 -4.6538792,-1.6786771 -4.6538794,1.6786768 0.158389,-4.94484188 -3.034645,-3.90736292 4.751769,-1.3774033 z"
|
||||
inkscape:transform-center-y="-0.6746144"
|
||||
transform="matrix(0.89226704,0,0,0.89226704,26.142673,19.933344)" />
|
||||
</g>
|
||||
<g
|
||||
id="g24501"
|
||||
transform="matrix(0.5277136,0,0,0.5277136,-6.1140045,0.6060986)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#f7b200;fill-opacity:1;stroke:none;stroke-width:2.56963;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||
id="path24497"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="-8.2018356"
|
||||
sodipodi:cy="-2.1710742"
|
||||
sodipodi:r1="7.9176526"
|
||||
sodipodi:r2="4.5684857"
|
||||
sodipodi:arg1="-1.5707963"
|
||||
sodipodi:arg2="-0.94247777"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -8.2018354,-10.088727 2.6852884,4.2216703 4.84484654,1.2492935 -3.18524704,3.85842889 0.3089911,4.99377581 -4.6538794,-1.8370299 -4.6538792,1.8370296 0.308991,-4.99377573 -3.185247,-3.85842907 4.844847,-1.2492932 z"
|
||||
inkscape:transform-center-y="0.31212486"
|
||||
transform="matrix(-0.41282678,0,0,-0.41282678,15.438508,17.099887)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:none;fill-opacity:1;stroke:#f7b200;stroke-width:0.56037;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill"
|
||||
id="path24499"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="-8.2018356"
|
||||
sodipodi:cy="-2.1710742"
|
||||
sodipodi:r1="7.9176526"
|
||||
sodipodi:r2="4.7268386"
|
||||
sodipodi:arg1="-1.5707963"
|
||||
sodipodi:arg2="-0.9424778"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -8.2018354,-10.088727 2.7783658,4.0935601 4.75176914,1.3774037 -3.03464454,3.90736248 0.1583886,4.94484222 -4.6538792,-1.6786771 -4.6538794,1.6786768 0.158389,-4.94484188 -3.034645,-3.90736292 4.751769,-1.3774033 z"
|
||||
inkscape:transform-center-y="-0.6746144"
|
||||
transform="matrix(0.89226704,0,0,0.89226704,26.142673,19.933344)" />
|
||||
</g>
|
||||
<g
|
||||
id="g24507"
|
||||
transform="matrix(0.5277136,0,0,0.5277136,1.5088777,0.58197555)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#f7b200;fill-opacity:1;stroke:none;stroke-width:2.56963;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||
id="path24503"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="-8.2018356"
|
||||
sodipodi:cy="-2.1710742"
|
||||
sodipodi:r1="7.9176526"
|
||||
sodipodi:r2="4.5684857"
|
||||
sodipodi:arg1="-1.5707963"
|
||||
sodipodi:arg2="-0.94247777"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -8.2018354,-10.088727 2.6852884,4.2216703 4.84484654,1.2492935 -3.18524704,3.85842889 0.3089911,4.99377581 -4.6538794,-1.8370299 -4.6538792,1.8370296 0.308991,-4.99377573 -3.185247,-3.85842907 4.844847,-1.2492932 z"
|
||||
inkscape:transform-center-y="0.31212486"
|
||||
transform="matrix(-0.41282678,0,0,-0.41282678,15.438508,17.099887)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:none;fill-opacity:1;stroke:#f7b200;stroke-width:0.56037;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill"
|
||||
id="path24505"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="-8.2018356"
|
||||
sodipodi:cy="-2.1710742"
|
||||
sodipodi:r1="7.9176526"
|
||||
sodipodi:r2="4.7268386"
|
||||
sodipodi:arg1="-1.5707963"
|
||||
sodipodi:arg2="-0.9424778"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -8.2018354,-10.088727 2.7783658,4.0935601 4.75176914,1.3774037 -3.03464454,3.90736248 0.1583886,4.94484222 -4.6538792,-1.6786771 -4.6538794,1.6786768 0.158389,-4.94484188 -3.034645,-3.90736292 4.751769,-1.3774033 z"
|
||||
inkscape:transform-center-y="-0.6746144"
|
||||
transform="matrix(0.89226704,0,0,0.89226704,26.142673,19.933344)" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.4 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c6lekbldg584j"
|
||||
path="res://.godot/imported/RJActionSequence.svg-861cbfffa33440d8d759203c534bd591.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Scripts/Rokojori/Rokojori-Action-Library/Icons/RJActionSequence.svg"
|
||||
dest_files=["res://.godot/imported/RJActionSequence.svg-861cbfffa33440d8d759203c534bd591.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -4,12 +4,31 @@ using Godot;
|
|||
|
||||
namespace Rokojori
|
||||
{
|
||||
/** <summary for="class ActionList">
|
||||
|
||||
<title>
|
||||
Executes multiple actions (RJAction) at once.
|
||||
</title>
|
||||
|
||||
<description>
|
||||
The ActionList, which is an RJAction itself, executes all actions stored in the member 'actions' and also child nodes
|
||||
that extend RJAction, when 'triggerDirectChildren' is checked.
|
||||
|
||||
</description>
|
||||
|
||||
</summary>
|
||||
*/
|
||||
|
||||
[GlobalClass, Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/RJActionList.svg") ]
|
||||
public partial class ActionList : RJAction
|
||||
{
|
||||
{
|
||||
|
||||
/** <summary for="field actions">Actions to execute</summary>*/
|
||||
[Export]
|
||||
public RJAction[] actions;
|
||||
[Export]
|
||||
|
||||
/** <summary for="field triggerDirectChildren">Whether to execute RJAction child nodes</summary>*/
|
||||
[Export]
|
||||
public bool triggerDirectChildren = true;
|
||||
|
||||
public override void _OnTrigger()
|
||||
|
@ -27,6 +46,9 @@ namespace Rokojori
|
|||
return;
|
||||
}
|
||||
|
||||
Nodes.ForEachDirectChild<RJAction>( this, a => Actions.Trigger( a ) );
|
||||
|
||||
/*
|
||||
var childCount = GetChildCount();
|
||||
|
||||
for ( int i = 0; i < childCount; i++ )
|
||||
|
@ -41,6 +63,7 @@ namespace Rokojori
|
|||
Actions.Trigger( action );
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
|
||||
using System.Diagnostics;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Godot;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class ActionSequenceRunner
|
||||
{
|
||||
public ActionSequence sequence;
|
||||
public List<RJAction> actions;
|
||||
public List<RJSequenceAction> sequencables;
|
||||
public int sequencablesIndex = 0;
|
||||
bool cancelled = false;
|
||||
bool cancelledSequence = false;
|
||||
|
||||
int _runID = -1;
|
||||
|
||||
RJSequenceAction _runningAction;
|
||||
int _runningActionID = -1;
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
if ( cancelled || _runningAction == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cancelled = true;
|
||||
_runningAction.CancelAction( _runningActionID );
|
||||
}
|
||||
|
||||
void CancelRun()
|
||||
{
|
||||
if ( cancelledSequence )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! cancelled )
|
||||
{
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
cancelledSequence = true;
|
||||
sequence.DispatchCancelled( _runID );
|
||||
sequence.ClearRun( this );
|
||||
}
|
||||
|
||||
|
||||
public void ProcessNext()
|
||||
{
|
||||
RJLog.Log( "@" + sequence.Name, "Index:", sequencablesIndex );
|
||||
|
||||
if ( sequencablesIndex == 0 )
|
||||
{
|
||||
_runID = sequence.DispatchStart();
|
||||
|
||||
if ( sequencables.Count == 0 )
|
||||
{
|
||||
actions.ForEach( a => Actions.Trigger( a ) );
|
||||
sequence.DispatchEnd( _runID );
|
||||
sequence.ClearRun( this );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( sequencablesIndex >= sequencables.Count )
|
||||
{
|
||||
TriggerAllAfter( sequencables[ sequencables.Count -1 ] );
|
||||
sequence.DispatchEnd( _runID );
|
||||
sequence.ClearRun( this );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( cancelled )
|
||||
{
|
||||
CancelRun();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var sequenceAction = sequencables[ sequencablesIndex ];
|
||||
StartAction( sequenceAction );
|
||||
}
|
||||
|
||||
Dictionary<RJSequenceAction,RJSequenceAction.OnSequenceDoneEventHandler> callbacks =
|
||||
new Dictionary<RJSequenceAction, RJSequenceAction.OnSequenceDoneEventHandler>();
|
||||
|
||||
void StartAction( RJSequenceAction action )
|
||||
{
|
||||
var capturedAction = action;
|
||||
|
||||
RJSequenceAction.OnSequenceDoneEventHandler callback =
|
||||
( long id, bool success ) =>
|
||||
{
|
||||
|
||||
//RJLog.Log( "On Done", id, success );
|
||||
|
||||
if ( id != _runningActionID )
|
||||
{
|
||||
// RJLog.Error( "Invalid ID", id, "!=", _runningActionID );
|
||||
return;
|
||||
}
|
||||
|
||||
_runningActionID = -1;
|
||||
|
||||
if ( success )
|
||||
{
|
||||
sequencablesIndex ++;
|
||||
ProcessNext();
|
||||
}
|
||||
else
|
||||
{
|
||||
sequence.DispatchCancelled( _runID );
|
||||
sequence.ClearRun( this );
|
||||
}
|
||||
|
||||
var callbackReference = callbacks[ capturedAction ];
|
||||
capturedAction.OnSequenceDone -= callbackReference;
|
||||
|
||||
callbacks.Remove( capturedAction );
|
||||
};
|
||||
|
||||
RunNext( action, callback );
|
||||
|
||||
/*
|
||||
_runningAction = action;
|
||||
callbacks[ _runningAction ] = callback;
|
||||
_runningAction.OnSequenceDone += callback;
|
||||
TriggerAllBefore( _runningAction );
|
||||
Actions.Trigger( _runningAction );
|
||||
_runningActionID = _runningAction.GetLastSequenceActionID();
|
||||
*/
|
||||
}
|
||||
|
||||
void RunNext( RJSequenceAction action, RJSequenceAction.OnSequenceDoneEventHandler callback )
|
||||
{
|
||||
_runningAction = action;
|
||||
callbacks[ _runningAction ] = callback;
|
||||
_runningAction.OnSequenceDone += callback;
|
||||
|
||||
|
||||
TriggerAllBefore( _runningAction );
|
||||
Actions.Trigger( _runningAction );
|
||||
_runningActionID = _runningAction.GetLastSequenceActionID();
|
||||
}
|
||||
|
||||
void TriggerAllBefore( RJSequenceAction action )
|
||||
{
|
||||
var actionIndex = actions.IndexOf( action );
|
||||
|
||||
for ( int i = actionIndex - 1; i >= 0; i -- )
|
||||
{
|
||||
if ( typeof( RJSequenceAction ).IsAssignableFrom( actions[ i ].GetType() ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RJLog.Log( "Triggering Action", actions[ i ].Name );
|
||||
|
||||
Actions.Trigger( actions[ i ] );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TriggerAllAfter( RJSequenceAction action )
|
||||
{
|
||||
var actionIndex = actions.IndexOf( action );
|
||||
|
||||
for ( int i = actionIndex + 1; i < actions.Count; i ++ )
|
||||
{
|
||||
if ( typeof( RJSequenceAction ).IsAssignableFrom( actions[ i ].GetType() ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RJLog.Log( "Triggering Action", actions[ i ].Name );
|
||||
Actions.Trigger( actions[ i ] );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[GlobalClass, Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/RJActionSequence.svg") ]
|
||||
public partial class ActionSequence:RJSequenceAction
|
||||
{
|
||||
/** <summary for="field actions">Actions to execute</summary>*/
|
||||
[Export]
|
||||
public RJAction[] actions;
|
||||
|
||||
[Export]
|
||||
public bool triggerDirectChildren = true;
|
||||
|
||||
List<ActionSequenceRunner> running = new List<ActionSequenceRunner>();
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
var run = new ActionSequenceRunner();
|
||||
run.sequence = this;
|
||||
run.actions = new List<RJAction>( actions );
|
||||
|
||||
if ( triggerDirectChildren )
|
||||
{
|
||||
Nodes.ForEachDirectChild<RJAction>( this, a => run.actions.Add( a ) );
|
||||
}
|
||||
|
||||
run.sequencables = Lists.FilterType<RJAction,RJSequenceAction>( run.actions );
|
||||
|
||||
|
||||
|
||||
|
||||
// RJLog.Log( "Running", HierarchyName.Of( this ), run.sequencables.Count );
|
||||
running.Add( run );
|
||||
run.ProcessNext();
|
||||
}
|
||||
|
||||
public override void CancelAction( int id )
|
||||
{
|
||||
running.ForEach( r => r.Cancel() );
|
||||
}
|
||||
|
||||
public void ClearRun( ActionSequenceRunner run )
|
||||
{
|
||||
running.Remove( run );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -11,7 +11,7 @@ namespace Rokojori
|
|||
if ( action == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
action.Trigger();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
using Godot;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[GlobalClass]
|
||||
public partial class Delay : RJSequenceAction
|
||||
{
|
||||
[Export]
|
||||
public float duration;
|
||||
|
||||
[Export]
|
||||
public string message;
|
||||
|
||||
float elapsed = 0;
|
||||
bool running = false;
|
||||
bool finished = false;
|
||||
|
||||
int runID = 0;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
/// OnSequenceDone += ( id, success ) => { RJLog.Log( "OnSequenceDone", Name ); } ;
|
||||
}
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
//RJLog.Log( "Starting", Name );
|
||||
runID = DispatchStart();
|
||||
|
||||
elapsed = 0;
|
||||
running = true;
|
||||
finished = false;
|
||||
|
||||
}
|
||||
|
||||
public override void _Process( double delta )
|
||||
{
|
||||
if ( ! running )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
elapsed += (float) delta;
|
||||
|
||||
if ( elapsed > duration )
|
||||
{
|
||||
running = false;
|
||||
finished = true;
|
||||
|
||||
RJLog.Log( Name, " >> '" + message + "'" );
|
||||
DispatchEnd( runID );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
class_name GDDelay
|
||||
extends RJSequenceAction
|
||||
|
||||
@export var duration = 1
|
||||
@export var message = "huhu"
|
||||
|
||||
var running = false
|
||||
var elapsed = 0;
|
||||
var id = 0;
|
||||
var cachedIDs = []
|
||||
|
||||
func _onTrigger():
|
||||
if running:
|
||||
cachedIDs
|
||||
|
||||
|
||||
running = true
|
||||
elapsed = 0
|
||||
id = dispatchStart()
|
||||
|
||||
func _process( delta ):
|
||||
if ! running:
|
||||
return
|
||||
elapsed += delta
|
||||
|
||||
if elapsed > duration:
|
||||
running = false
|
||||
elapsed = 0
|
||||
print( name + " >> " + message )
|
||||
dispatchEnd( id )
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[GlobalClass]
|
||||
public partial class LoadScene : RJSequenceAction
|
||||
{
|
||||
[Export]
|
||||
public string scenePath;
|
||||
|
||||
[Export]
|
||||
public Node target;
|
||||
|
||||
[Export]
|
||||
public RJAction onLoaded;
|
||||
|
||||
bool _loading = false;
|
||||
int _id = -1;
|
||||
List<int> _cached = new List<int>();
|
||||
|
||||
public override void _OnTrigger()
|
||||
{
|
||||
if ( _loading )
|
||||
{
|
||||
_cached.Add( DispatchStart() );
|
||||
return;
|
||||
}
|
||||
|
||||
_loading = true;
|
||||
_id = DispatchStart();
|
||||
var errorCode = ResourceLoader.LoadThreadedRequest( scenePath );
|
||||
|
||||
if ( Error.Ok != errorCode )
|
||||
{
|
||||
_cached.Add( _id );
|
||||
_id = -1;
|
||||
_loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void _Process ( double delta )
|
||||
{
|
||||
if ( ! _loading )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var status = ResourceLoader.LoadThreadedGetStatus( scenePath );
|
||||
|
||||
if ( ResourceLoader.ThreadLoadStatus.Loaded != status )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_loading = false;
|
||||
|
||||
var packedScene = (PackedScene) ResourceLoader.LoadThreadedGet( scenePath );
|
||||
var node = packedScene.Instantiate();
|
||||
target.AddChild( node );
|
||||
Actions.Trigger( onLoaded );
|
||||
DispatchEnd( _id );
|
||||
_id = -1;
|
||||
|
||||
_cached.ForEach(
|
||||
( c )=>
|
||||
{
|
||||
target.AddChild( packedScene.Instantiate() );
|
||||
Actions.Trigger( onLoaded );
|
||||
DispatchEnd( c );
|
||||
}
|
||||
);
|
||||
|
||||
_cached.Clear();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ namespace Rokojori
|
|||
|
||||
public override void _Ready()
|
||||
{
|
||||
RJLog.Log( "OnReady" );
|
||||
// RJLog.Log( "OnReady" );
|
||||
Actions.Trigger( action );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
|
||||
public struct HSLColor
|
||||
{
|
||||
public static readonly HSLColor white = new HSLColor( new Color( 1, 1, 1, 1 ) );
|
||||
public static readonly HSLColor red = new HSLColor( new Color( 1, 0, 0, 1 ) );
|
||||
public static readonly HSLColor green = new HSLColor( new Color( 0, 1, 0, 1 ) );
|
||||
public static readonly HSLColor blue = new HSLColor( new Color( 0, 0, 1, 1 ) );
|
||||
public static readonly HSLColor black = new HSLColor( new Color( 0, 0, 0, 1 ) );
|
||||
|
||||
// hue in degrees 0 - 360
|
||||
public float h;
|
||||
// saturation 0 - 1
|
||||
public float s;
|
||||
// luminance 0 - 1
|
||||
public float l;
|
||||
// alpha 0 - 1
|
||||
public float a;
|
||||
|
||||
|
||||
public HSLColor( float h, float s, float l, float a )
|
||||
{
|
||||
this.h = h;
|
||||
this.s = s;
|
||||
this.l = l;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public HSLColor( float h, float s, float l )
|
||||
{
|
||||
this.h = h;
|
||||
this.s = s;
|
||||
this.l = l;
|
||||
this.a = 1f;
|
||||
}
|
||||
|
||||
public float lumaBT601
|
||||
{
|
||||
get
|
||||
{
|
||||
var rgb = ToRGBA();
|
||||
|
||||
var R = rgb.R;
|
||||
var G = rgb.G;
|
||||
var B = rgb.B;
|
||||
|
||||
return 0.2989f * R + 0.5870f * G + 0.1140f * B;
|
||||
}
|
||||
}
|
||||
|
||||
public float lumaBT709
|
||||
{
|
||||
get
|
||||
{
|
||||
var rgb = ToRGBA();
|
||||
|
||||
var R = rgb.R;
|
||||
var G = rgb.G;
|
||||
var B = rgb.B;
|
||||
|
||||
return 0.2126f * R + 0.7152f * G + 0.0722f * B;
|
||||
}
|
||||
}
|
||||
|
||||
public float normalizedHue
|
||||
{
|
||||
get { return h / 360f;}
|
||||
}
|
||||
|
||||
public HSLColor( Color c )
|
||||
{
|
||||
HSLColor temp = FromRGBA( c );
|
||||
h = temp.h;
|
||||
s = temp.s;
|
||||
l = temp.l;
|
||||
a = temp.a;
|
||||
}
|
||||
|
||||
public static HSLColor FromRGBA( Color c )
|
||||
{
|
||||
float h, s, l, a;
|
||||
a = c.A;
|
||||
|
||||
float cmin = Mathf.Min( Mathf.Min( c.R, c.G ), c.B );
|
||||
float cmax = Mathf.Max( Mathf.Max( c.R, c.G ), c.B );
|
||||
|
||||
l = ( cmin + cmax ) / 2f;
|
||||
|
||||
if ( cmin == cmax )
|
||||
{
|
||||
s = 0;
|
||||
h = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float delta = cmax - cmin;
|
||||
|
||||
s = ( l <= .5f ) ? ( delta / ( cmax + cmin ) ) : ( delta / ( 2f - ( cmax + cmin ) ) );
|
||||
|
||||
h = 0;
|
||||
|
||||
if ( c.R == cmax )
|
||||
{
|
||||
h = ( c.G - c.B ) / delta;
|
||||
}
|
||||
else if ( c.G == cmax )
|
||||
{
|
||||
h = 2f + ( c.B - c.R ) / delta;
|
||||
}
|
||||
else if ( c.B == cmax )
|
||||
{
|
||||
h = 4f + ( c.R - c.G ) / delta;
|
||||
}
|
||||
|
||||
h = MathX.Repeat( h * 60f, 360f );
|
||||
}
|
||||
|
||||
return new HSLColor( h, s, l, a );
|
||||
}
|
||||
|
||||
public static HSLColor Lerp( HSLColor x, HSLColor y, float t )
|
||||
{
|
||||
float h = Mathf.LerpAngle( x.h, y.h, t );
|
||||
float s = Mathf.Lerp( x.s, y.s, t );
|
||||
float l = Mathf.Lerp( x.l, y.l, t );
|
||||
float a = Mathf.Lerp( x.a, y.a, t );
|
||||
|
||||
return new HSLColor( h, s, l, a );
|
||||
}
|
||||
|
||||
public static Color Lerp( Color x, Color y, float t )
|
||||
{
|
||||
var hx = new HSLColor( x );
|
||||
var hy = new HSLColor( y );
|
||||
|
||||
var h = Lerp( hx, hy, t );
|
||||
|
||||
return h.ToRGBA();
|
||||
}
|
||||
|
||||
|
||||
public Color ToRGBA( )
|
||||
{
|
||||
float r, g, b, a;
|
||||
a = this.a;
|
||||
|
||||
float m1, m2;
|
||||
|
||||
m2 = ( l <= .5f ) ? ( l * ( 1f + s ) ) : ( l + s - l * s );
|
||||
m1 = 2f * l - m2;
|
||||
|
||||
if ( s == 0f )
|
||||
{
|
||||
r = g = b = l;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = Value( m1, m2, h + 120f );
|
||||
g = Value( m1, m2, h );
|
||||
b = Value( m1, m2, h - 120f );
|
||||
}
|
||||
|
||||
return new Color( r, g, b, a );
|
||||
}
|
||||
|
||||
static float Value( float n1, float n2, float hue )
|
||||
{
|
||||
|
||||
hue = MathX.Repeat( hue, 360f );
|
||||
|
||||
if ( hue < 60f )
|
||||
{
|
||||
return n1 + ( n2 - n1 ) * hue / 60f;
|
||||
}
|
||||
else if ( hue < 180f )
|
||||
{
|
||||
return n2;
|
||||
}
|
||||
else if ( hue < 240f )
|
||||
{
|
||||
return n1 + ( n2 - n1 ) * ( 240f - hue ) / 60f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return n1;
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator HSLColor( Color src )
|
||||
{
|
||||
return FromRGBA( src );
|
||||
}
|
||||
|
||||
public static implicit operator Color( HSLColor src )
|
||||
{
|
||||
return src.ToRGBA( );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public enum FilePathType
|
||||
{
|
||||
ABSOLUTE,
|
||||
RELATIVE
|
||||
}
|
||||
|
||||
public class FilePath
|
||||
{
|
||||
public FilePathType type;
|
||||
public FilePath parent;
|
||||
public string path;
|
||||
|
||||
public string _fileName;
|
||||
public string _fileExtension;
|
||||
string _fullPath = null;
|
||||
|
||||
/**<summary for="method get_fileName">Only fileName without fileExtension </summary>*/
|
||||
public string fileName
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( _fileName == null)
|
||||
{
|
||||
_fileName = Path.GetFileNameWithoutExtension( path );
|
||||
}
|
||||
|
||||
return _fileName;
|
||||
}
|
||||
}
|
||||
|
||||
/**<summary for="method get_fullFileName">Combines fileName + fileExtension </summary>*/
|
||||
public string fullFileName => fileName + fileExtension;
|
||||
|
||||
/**<summary for="method get_fileExtension>File extension including the dot, eg. ".svg", ".jpg", ".js"</summary>*/
|
||||
public string fileExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( _fileExtension == null)
|
||||
{
|
||||
_fileExtension = Path.GetExtension( path );
|
||||
}
|
||||
|
||||
return _fileExtension;
|
||||
}
|
||||
}
|
||||
|
||||
public string fullPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( _fullPath == null )
|
||||
{
|
||||
if ( this.parent != null )
|
||||
{
|
||||
_fullPath = this.parent.fullPath + "/" + path;
|
||||
}
|
||||
else
|
||||
{
|
||||
_fullPath = path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return _fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
public static FilePath Create( string path, FilePathType type = FilePathType.RELATIVE, FilePath parent = null )
|
||||
{
|
||||
var rp = new FilePath();
|
||||
|
||||
rp.type = type;
|
||||
rp.parent = parent;
|
||||
rp.path = path;
|
||||
|
||||
return rp;
|
||||
}
|
||||
|
||||
public static FilePath Absolute( string path )
|
||||
{
|
||||
return FilePath.Create( path, FilePathType.ABSOLUTE );
|
||||
}
|
||||
|
||||
public FilePath MakeRelative( string path )
|
||||
{
|
||||
return FilePath.Create( path, FilePathType.RELATIVE, this );
|
||||
}
|
||||
|
||||
public FilePath MakeAbsolutePathRelative( string absolutePath )
|
||||
{
|
||||
absolutePath = FilePath.Normalize( absolutePath );
|
||||
var ownAbsolutePath = FilePath.Normalize( fullPath );
|
||||
|
||||
if ( ! absolutePath.StartsWith( ownAbsolutePath ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var relativePath = absolutePath.Substring( ownAbsolutePath.Length );
|
||||
|
||||
return MakeRelative( relativePath );
|
||||
}
|
||||
|
||||
public FilePath CreateRelativeFromAbsolutePathWithParent( string absolutePath, FilePath absoluteParent )
|
||||
{
|
||||
var rp = new FilePath();
|
||||
|
||||
rp.type = FilePathType.RELATIVE;
|
||||
rp.parent = absoluteParent;
|
||||
rp.path = Path.GetFileName( absolutePath );
|
||||
rp._fullPath = absolutePath;
|
||||
|
||||
return rp;
|
||||
|
||||
}
|
||||
|
||||
public static string Normalize( string path, bool removeFirst = true, bool removeLast = true )
|
||||
{
|
||||
path = Regex.Replace( path, @"\\", "/" );
|
||||
path = Regex.Replace( path, @"/+", "/" );
|
||||
|
||||
if ( removeFirst )
|
||||
{
|
||||
path = Regex.Replace( path, @"^/", "" );
|
||||
}
|
||||
|
||||
if ( removeLast )
|
||||
{
|
||||
path = Regex.Replace( path, @"/$", "" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string Join( params string[] paths )
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for ( int i = 0; i < paths.Length; i++ )
|
||||
{
|
||||
var path = paths[ i ];
|
||||
|
||||
path = Normalize( path );
|
||||
|
||||
if ( i != 0 )
|
||||
{
|
||||
sb.Append( "/" );
|
||||
}
|
||||
|
||||
sb.Append( path );
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class FilesSync
|
||||
{
|
||||
public static bool DirectoryExists( string path )
|
||||
{
|
||||
return Directory.Exists( path );
|
||||
}
|
||||
|
||||
public static void CreateDirectory( string path )
|
||||
{
|
||||
Directory.CreateDirectory( path );
|
||||
}
|
||||
|
||||
public static void EnsureDirectoryExists( string path )
|
||||
{
|
||||
if ( DirectoryExists( path ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CreateDirectory( path );
|
||||
}
|
||||
|
||||
public static List<FilePath> GetDirectories( string path, System.Func<FilePath,bool> evaluator = null )
|
||||
{
|
||||
var list = new List<FilePath>();
|
||||
|
||||
var files = Directory.GetDirectories( path, "*", SearchOption.TopDirectoryOnly );
|
||||
|
||||
var absolutePath = FilePath.Absolute( path );
|
||||
|
||||
for ( int i = 0; i < files.Length; i++ )
|
||||
{
|
||||
var fileName = Path.GetFileName( files[ i ] );
|
||||
|
||||
var relativePath = absolutePath.MakeRelative( fileName );
|
||||
|
||||
if ( evaluator == null || evaluator( relativePath ) )
|
||||
{
|
||||
list.Add( relativePath );
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<FilePath> GetFiles( string path, System.Func<FilePath,bool> evaluator = null, bool recursive = false )
|
||||
{
|
||||
var list = new List<FilePath>();
|
||||
|
||||
var files = Directory.GetFiles( path, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly );
|
||||
|
||||
|
||||
var absoluteFilePath = FilePath.Absolute( path );
|
||||
|
||||
for ( int i = 0; i < files.Length; i++ )
|
||||
{
|
||||
var relativePath = absoluteFilePath.MakeAbsolutePathRelative( files[ i ] );
|
||||
|
||||
if ( evaluator == null || evaluator( relativePath ) )
|
||||
{
|
||||
list.Add( relativePath );
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static bool SaveUTF8( string path, string data )
|
||||
{
|
||||
try
|
||||
{
|
||||
using ( StreamWriter streamWriter = File.CreateText( path ) )
|
||||
{
|
||||
streamWriter.Write( data );
|
||||
}
|
||||
}
|
||||
catch ( System.Exception e )
|
||||
{
|
||||
RJLog.Log( "Could not save text: ", path, "exception", e );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public static bool SaveJSON( string path, object obj )
|
||||
{
|
||||
return SaveUTF8( path, JSON.StringifyObject( obj ) );
|
||||
}
|
||||
|
||||
public static string LoadUTF8( string path )
|
||||
{
|
||||
try
|
||||
{
|
||||
using ( StreamReader streamReader = File.OpenText( path ) )
|
||||
{
|
||||
return streamReader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
catch ( System.Exception e )
|
||||
{
|
||||
RJLog.Log( "Could not load text: ", path, "exception", e );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool SaveBytes( string fileName, byte[] byteArray )
|
||||
{
|
||||
try
|
||||
{
|
||||
using ( var fs = new FileStream( fileName, FileMode.Create, FileAccess.Write ) )
|
||||
{
|
||||
fs.Write( byteArray, 0, byteArray.Length );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch ( System.Exception e )
|
||||
{
|
||||
RJLog.Log( "Exception caught in process: {0}", e );
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -37,13 +37,35 @@ namespace Rokojori
|
|||
|
||||
if ( node is T )
|
||||
{
|
||||
return (T)node;
|
||||
return (T) node;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void ForEachDirectChild<T>( Node parent, System.Action<T> action ) where T:Node
|
||||
{
|
||||
if ( parent == null || action == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var numChildren = parent.GetChildCount();
|
||||
|
||||
for ( int i = 0; i < numChildren; i++ )
|
||||
{
|
||||
var node = parent.GetChild( i );
|
||||
|
||||
if ( ! ( node is T ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
action( node as T );
|
||||
}
|
||||
}
|
||||
|
||||
static NodesWalker nodesWalker = new NodesWalker();
|
||||
|
||||
public static T GetAnyChild<T>( Node parent ) where T:Node
|
||||
|
@ -63,6 +85,8 @@ namespace Rokojori
|
|||
SetState.SetStateOfNode( NodeStateType.Disabled, n, affectProcess, affectPhysicsProcess, affectInput );
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
public static void Iterate( Node[] nodes, System.Action<Node> callback )
|
||||
{
|
||||
for ( int i = 0; i < nodes.Length; i++ )
|
||||
|
@ -71,7 +95,7 @@ namespace Rokojori
|
|||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,12 +12,64 @@ namespace Rokojori
|
|||
|
||||
static void Stringify( object obj, StringBuilder output )
|
||||
{
|
||||
if ( obj == null )
|
||||
{
|
||||
output.Append( "<null>" );
|
||||
return;
|
||||
}
|
||||
|
||||
output.Append( obj.ToString() );
|
||||
}
|
||||
|
||||
static void LogMessage( string message )
|
||||
{
|
||||
GD.Print( message );
|
||||
var trace = GetTrace();
|
||||
GD.PrintRich("[b]" + message + "[/b]");
|
||||
GD.PrintRich( trace );
|
||||
}
|
||||
|
||||
static void LogErrorMessage( string message )
|
||||
{
|
||||
var trace = GetTrace();
|
||||
GD.PrintErr( message );
|
||||
GD.PrintRich( trace );
|
||||
}
|
||||
|
||||
static string GetTrace()
|
||||
{
|
||||
var stackTrace = new System.Diagnostics.StackTrace( true );
|
||||
var frameIndex = 3;
|
||||
|
||||
var frame = stackTrace.GetFrame( frameIndex );
|
||||
|
||||
var className = frame.GetFileName();
|
||||
|
||||
if ( className != null )
|
||||
{
|
||||
var slashIndex = -1;
|
||||
for ( int i = className.Length - 1; i >= 0 && slashIndex == -1; i-- )
|
||||
{
|
||||
if ( className[ i ] == '\\' || className[ i ] == '/' )
|
||||
{
|
||||
slashIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if ( slashIndex != -1 )
|
||||
{
|
||||
var end = className.EndsWith( ".cs" ) ? className.Length - 3 : className.Length;
|
||||
className = className.Substring( slashIndex + 1, end - ( slashIndex + 1 ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( className == null )
|
||||
{
|
||||
className ="<Unknown>";
|
||||
}
|
||||
|
||||
|
||||
var trace = className + "." + frame.GetMethod().Name + "() ln."+ frame.GetFileLineNumber();
|
||||
return "[color=gray] " + trace + "[/color]" ;
|
||||
}
|
||||
|
||||
public static void Log( params object[] objects)
|
||||
|
@ -36,6 +88,23 @@ namespace Rokojori
|
|||
|
||||
LogMessage( sb.ToString() );
|
||||
}
|
||||
|
||||
public static void Error( params object[] objects)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for ( int i = 0; i < objects.Length; i++ )
|
||||
{
|
||||
if ( i != 0 )
|
||||
{
|
||||
sb.Append( " " );
|
||||
}
|
||||
|
||||
Stringify( objects[ i ], sb );
|
||||
}
|
||||
|
||||
LogErrorMessage( sb.ToString() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using System.Text;
|
||||
using System;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class MathX
|
||||
{
|
||||
|
||||
public static float Clamp01( float value )
|
||||
{
|
||||
return Mathf.Clamp( value, 0, 1 );
|
||||
}
|
||||
|
||||
public static float Normalize( float value, float min, float max )
|
||||
{
|
||||
return ( value - min ) / ( max - min );
|
||||
}
|
||||
|
||||
public static float NormalizeClamped( float value, float min, float max )
|
||||
{
|
||||
return MathX.Clamp01( Normalize( value, min, max ) );
|
||||
}
|
||||
|
||||
public static float Map( float value, float inputMin, float inputMax,
|
||||
float outputMin, float outputMax )
|
||||
{
|
||||
var normalized = Normalize( value, inputMin, inputMax );
|
||||
return normalized * ( outputMax - outputMin ) + outputMin;
|
||||
}
|
||||
|
||||
public static float MapPositive( float value, float inputMax, float outputMax )
|
||||
{
|
||||
return Map( value, 0, inputMax, 0, outputMax );
|
||||
}
|
||||
|
||||
public static float MapClamped( float value, float inputMin, float inputMax,
|
||||
float outputMin, float outputMax )
|
||||
{
|
||||
var normalized = NormalizeClamped( value, inputMin, inputMax );
|
||||
return normalized * ( outputMax - outputMin ) + outputMin;
|
||||
}
|
||||
|
||||
public static float Map01( float value, float outputMin, float outputMax )
|
||||
{
|
||||
return value * ( outputMax - outputMin ) + outputMin;
|
||||
}
|
||||
|
||||
public static float MapPolar( float value, float min, float max )
|
||||
{
|
||||
return Map01( value * 0.5f + 0.5f, min, max );
|
||||
}
|
||||
|
||||
public static float MapPolarTo01( float value)
|
||||
{
|
||||
return value * 0.5f + 0.5f;
|
||||
}
|
||||
|
||||
public static float Repeat( float value, float length )
|
||||
{
|
||||
while ( value > length )
|
||||
{
|
||||
value -=length;
|
||||
}
|
||||
|
||||
while ( value < 0 )
|
||||
{
|
||||
value += length;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static float Triangle( float value )
|
||||
{
|
||||
value = MathX.Repeat( value, 1 ) * 2;
|
||||
|
||||
if ( value > 1 )
|
||||
{
|
||||
value = 2 - value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static float EaseSine( float value )
|
||||
{
|
||||
return Mathf.Sin( Mathf.Pi * ( value * 0.5f + 1.5f ) ) + 1;
|
||||
}
|
||||
|
||||
public static int Sign( int value )
|
||||
{
|
||||
return value == 0 ? 0 : value < 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
|
||||
public static T LerpList<T>( List<T> data, float t, Func<T,T,float,T> lerpElementsFunction,
|
||||
int dataFillSize = -1 )
|
||||
{
|
||||
dataFillSize = dataFillSize == -1 ? data.Count : dataFillSize;
|
||||
var floatIndex = t * dataFillSize;
|
||||
|
||||
if ( floatIndex <= 0 )
|
||||
{
|
||||
return data[ 0 ];
|
||||
}
|
||||
|
||||
if ( floatIndex >= ( dataFillSize - 1 ) )
|
||||
{
|
||||
return data[ dataFillSize - 1 ];
|
||||
}
|
||||
|
||||
var flooredIndex = Mathf.FloorToInt( floatIndex );
|
||||
var ceiledIndex = flooredIndex + 1;
|
||||
|
||||
var flooredValue = data[ flooredIndex ];
|
||||
var ceiledValue = data[ ceiledIndex ];
|
||||
|
||||
var interpolationAmount = floatIndex - flooredIndex;
|
||||
|
||||
return lerpElementsFunction( flooredValue, ceiledValue, interpolationAmount );
|
||||
|
||||
}
|
||||
|
||||
public static float PolarTriangle( float value )
|
||||
{
|
||||
return Triangle( value ) * 2f - 1f;
|
||||
}
|
||||
|
||||
public static float Step( float phase, float phaseStart, float phaseEnd )
|
||||
{
|
||||
if ( phase < phaseStart )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( phase >= phaseEnd )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ( phase - phaseStart ) / ( phaseStart - phaseEnd );
|
||||
}
|
||||
|
||||
|
||||
public static int Repeat( int value, int range )
|
||||
{
|
||||
while ( value < 0 )
|
||||
{
|
||||
value += range;
|
||||
}
|
||||
|
||||
while ( value >= range )
|
||||
{
|
||||
value -= range;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static float PolarRepeat( float value, float range )
|
||||
{
|
||||
while ( value < -range )
|
||||
{
|
||||
value += range * 2;
|
||||
}
|
||||
|
||||
while ( value >= range )
|
||||
{
|
||||
value -= range * 2;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
public static float Exponent( float base_, float power )
|
||||
{
|
||||
return Mathf.Log( power ) / Mathf.Log( base_ );
|
||||
}
|
||||
|
||||
public static float Base( float exponent, float power )
|
||||
{
|
||||
return Mathf.Pow( power, 1f / exponent );
|
||||
}
|
||||
|
||||
public static float SmoothingCoefficient( float ms, float reachingTarget = 0.1f, float frameDurationMS = 16.66666666f )
|
||||
{
|
||||
return 1f - Base( ms / frameDurationMS, reachingTarget );
|
||||
}
|
||||
|
||||
public static float SmoothValue( float oldValue, float newValue, float ms, float reachingTarget = 0.1f, float frameDurationMS = 16.66666666f )
|
||||
{
|
||||
return oldValue + SmoothingCoefficient( ms, reachingTarget, frameDurationMS ) * ( newValue - oldValue );
|
||||
}
|
||||
|
||||
public static float SmoothDegrees( float oldValue, float newValue, float ms, float reachingTarget = 0.1f, float frameDurationMS = 16.66666666f )
|
||||
{
|
||||
oldValue = Mathf.Wrap( oldValue, 0, 360 );
|
||||
newValue = Mathf.Wrap( newValue, 0, 360 );
|
||||
|
||||
var difference = newValue - oldValue;
|
||||
|
||||
if ( Mathf.Abs( difference ) > 180 )
|
||||
{
|
||||
if ( newValue > oldValue )
|
||||
{
|
||||
oldValue += 360;
|
||||
}
|
||||
else
|
||||
{
|
||||
newValue += 360;
|
||||
}
|
||||
}
|
||||
|
||||
return oldValue + SmoothingCoefficient( ms, reachingTarget, frameDurationMS ) * ( newValue - oldValue );
|
||||
}
|
||||
|
||||
public static Vector3 SmoothVector3( Vector3 oldValue, Vector3 newValue, float ms, float reachingTarget = 0.1f, float frameDurationMS = 16.66666666f )
|
||||
{
|
||||
return oldValue + SmoothingCoefficient( ms, reachingTarget, frameDurationMS ) * ( newValue - oldValue );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
[System.Serializable]
|
||||
public class LCGSeedState
|
||||
{
|
||||
public int modulus;
|
||||
public int multiplier;
|
||||
public int increment;
|
||||
public int state;
|
||||
}
|
||||
|
||||
public class LCG:SeedableRandomEngine
|
||||
{
|
||||
private LCGSeedState _seedState = new LCGSeedState();
|
||||
private float _normalizer;
|
||||
|
||||
public LCG()
|
||||
{
|
||||
this.setParameters( (int)Mathf.Pow(2,32), 1664525, 1013904223 );
|
||||
}
|
||||
|
||||
public static LCG Randomized()
|
||||
{
|
||||
var ms = Time.GetUnixTimeFromSystem() % 1;
|
||||
var seed = (int) ( ms * 10000 );
|
||||
var lcg = new LCG();
|
||||
RJLog.Log( "Seed", seed, Time.GetUnixTimeFromSystem() );
|
||||
lcg.SetSeed( seed );
|
||||
lcg.Next();
|
||||
return lcg;
|
||||
|
||||
}
|
||||
|
||||
void setParameters( int modulus, int multiplier, int increment )
|
||||
{
|
||||
_seedState.modulus = modulus;
|
||||
_seedState.multiplier = multiplier;
|
||||
_seedState.increment = increment;
|
||||
_seedState.state = 0;
|
||||
this._normalizer = 1f/(_seedState.modulus);
|
||||
}
|
||||
|
||||
public override object GetSeedState()
|
||||
{
|
||||
var seedState = new LCGSeedState();
|
||||
|
||||
seedState.modulus = _seedState.modulus;
|
||||
seedState.multiplier = _seedState.multiplier;
|
||||
seedState.increment = _seedState.increment;
|
||||
seedState.state = _seedState.state;
|
||||
|
||||
|
||||
return seedState;
|
||||
}
|
||||
|
||||
public override void SetSeedState( object seedStateObject )
|
||||
{
|
||||
var seedState = (LCGSeedState) seedStateObject;
|
||||
|
||||
_seedState.modulus = seedState.modulus;
|
||||
_seedState.multiplier = seedState.multiplier;
|
||||
_seedState.increment = seedState.increment;
|
||||
_seedState.state = seedState.state;
|
||||
this._normalizer = 1 / ( _seedState.modulus );
|
||||
}
|
||||
|
||||
public override float Next()
|
||||
{
|
||||
_seedState.state = ( _seedState.state * _seedState.multiplier + _seedState.increment) % _seedState.modulus;
|
||||
var value = _seedState.state * this._normalizer;
|
||||
return Mathf.Abs( value );
|
||||
}
|
||||
|
||||
public override void SetSeed( int seed )
|
||||
{
|
||||
_seedState.state = seed % _seedState.modulus;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public abstract class RandomEngine
|
||||
{
|
||||
public abstract float Next();
|
||||
|
||||
public float Value( float scalar )
|
||||
{
|
||||
return Next() * scalar;
|
||||
}
|
||||
|
||||
public float Range( float a, float b)
|
||||
{
|
||||
return this.Next()*( b - a ) + a;
|
||||
}
|
||||
|
||||
public float Polar()
|
||||
{
|
||||
return this.Next() * 2f - 1f;
|
||||
}
|
||||
|
||||
public float Polar( float value )
|
||||
{
|
||||
return Polar() * value;
|
||||
}
|
||||
|
||||
public bool Bool()
|
||||
{
|
||||
return this.Next() > 0.5f;
|
||||
}
|
||||
|
||||
|
||||
public bool Chance( float value )
|
||||
{
|
||||
return value / 100f >= Next();
|
||||
}
|
||||
|
||||
public bool FlipCoin()
|
||||
{
|
||||
return Chance( 50f );
|
||||
}
|
||||
|
||||
public float PercentageVariation( float variation )
|
||||
{
|
||||
var value = ( 100f - variation ) / 100f;
|
||||
return Range( value, 1f / value );
|
||||
}
|
||||
|
||||
public bool WithChanceOf( float value )
|
||||
{
|
||||
return ( this.Next() * 100 ) <= value ;
|
||||
}
|
||||
|
||||
public Vector3 Between( Vector3 a, Vector3 b )
|
||||
{
|
||||
return a.Lerp( b, this.Next() );
|
||||
}
|
||||
|
||||
public Color RandomHue( float saturation = 1f, float luminance = 0.5f)
|
||||
{
|
||||
var hue = Range( 0, 360 );
|
||||
var color = new HSLColor( hue, saturation, luminance );
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
public Vector2 InRectangle( Vector2 min, Vector2 max )
|
||||
{
|
||||
var x = Mathf.Lerp( min.X, max.X, Next() );
|
||||
var y = Mathf.Lerp( min.Y, max.Y, Next() );
|
||||
|
||||
return new Vector2( x, y );
|
||||
}
|
||||
|
||||
public Vector3 InCube()
|
||||
{
|
||||
return new Vector3( Polar(), Polar(), Polar() );
|
||||
}
|
||||
|
||||
public Vector3 InsideCube( float size )
|
||||
{
|
||||
return InCube() * ( size * 0.5f );
|
||||
}
|
||||
|
||||
public Vector3 InsideSphere()
|
||||
{
|
||||
var inCube = InCube();
|
||||
|
||||
if ( inCube.LengthSquared() > 1 )
|
||||
{
|
||||
inCube = inCube.Normalized() * Next();
|
||||
}
|
||||
|
||||
return inCube;
|
||||
}
|
||||
|
||||
public Vector3 OnSphere( float size )
|
||||
{
|
||||
return OnSphere() * size;
|
||||
}
|
||||
|
||||
public Vector3 OnSphere()
|
||||
{
|
||||
var dir = InsideSphere();
|
||||
|
||||
while ( dir.LengthSquared() == 0 )
|
||||
{
|
||||
dir = InsideSphere();
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
|
||||
public Vector3 InSphere( float size )
|
||||
{
|
||||
return InsideSphere() * ( size * 0.5f );
|
||||
}
|
||||
|
||||
public int IntegerInclusive( int min, int max )
|
||||
{
|
||||
return (int) ( Mathf.Floor( this.Next() * ( max - min + 1 ) ) + min ) ;
|
||||
}
|
||||
|
||||
public int IntegerInclusive( int max )
|
||||
{
|
||||
return IntegerInclusive( 0, max );
|
||||
}
|
||||
|
||||
|
||||
public int IntegerExclusive( int min, int max )
|
||||
{
|
||||
var nextValue = this.Next();
|
||||
var randomValue = nextValue * ( max - min ) + min;
|
||||
|
||||
var value = (int) ( Mathf.Floor( randomValue ) );
|
||||
return Mathf.Min( max - 1, value );
|
||||
}
|
||||
|
||||
public int IntegerExclusive( int max )
|
||||
{
|
||||
return IntegerExclusive( 0, max );
|
||||
}
|
||||
|
||||
|
||||
public char From( string source )
|
||||
{
|
||||
var index = IntegerExclusive( source.Length );
|
||||
return source[ index ];
|
||||
}
|
||||
|
||||
public T From<T>( T[] array )
|
||||
{
|
||||
if ( array.Length == 0 )
|
||||
{
|
||||
return default ( T );
|
||||
}
|
||||
var index = IntegerExclusive( array.Length );
|
||||
return array[ index ];
|
||||
}
|
||||
|
||||
public T From<T>( HashSet<T> set )
|
||||
{
|
||||
var selectedIndex = IntegerExclusive( set.Count );
|
||||
|
||||
var currentIndex = 0;
|
||||
|
||||
foreach ( var e in set )
|
||||
{
|
||||
if ( selectedIndex == currentIndex )
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
return default( T );
|
||||
}
|
||||
|
||||
public T FromValues<T>( T first, params T[] values )
|
||||
{
|
||||
if ( values.Length == 0 )
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
var index = IntegerExclusive( values.Length + 1 );
|
||||
|
||||
return index == 0 ? first : values[ index - 1 ];
|
||||
}
|
||||
|
||||
public T From<T>( List<T> list )
|
||||
{
|
||||
if ( list.Count == 0 )
|
||||
{
|
||||
return default ( T );
|
||||
}
|
||||
|
||||
var index = this.IntegerExclusive( 0, list.Count );
|
||||
|
||||
return list[ index ];
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class SeedableRandomEngine:RandomEngine
|
||||
{
|
||||
public abstract void SetSeed( int number );
|
||||
public abstract object GetSeedState();
|
||||
public abstract void SetSeedState( object seedState );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
|
||||
public class JSON
|
||||
{
|
||||
public static string Stringify( JSONData jsonData )
|
||||
{
|
||||
return jsonData.Stringify();
|
||||
}
|
||||
|
||||
public static JSONData Parse( string jsonString )
|
||||
{
|
||||
return new JSONParser().Parse( jsonString );
|
||||
}
|
||||
|
||||
public static string StringifyObject( object value )
|
||||
{
|
||||
var serializer = new JSONSerializer( new JSONSerializationSettings() );
|
||||
return serializer.Serialize( value );
|
||||
}
|
||||
|
||||
public static T ParseObject<T>( string jsonString ) where T:new()
|
||||
{
|
||||
var deserializer = new JSONDeserializer( new JSONSerializationSettings() );
|
||||
return deserializer.Deserialize<T>( jsonString );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class JSONArray:JSONData
|
||||
{
|
||||
List<JSONData> _values = new List<JSONData>();
|
||||
|
||||
public override JSONDataType dataType
|
||||
{
|
||||
get { return JSONDataType.ARRAY; }
|
||||
}
|
||||
|
||||
public List<JSONData> values { get { return _values; }}
|
||||
|
||||
public int size { get { return _values.Count; } }
|
||||
|
||||
public JSONData Get( int index )
|
||||
{
|
||||
return _values[ index ];
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void Delete( int index )
|
||||
{
|
||||
_values.RemoveAt( index );
|
||||
}
|
||||
|
||||
public void Push( JSONData value ) {_values.Add( value );}
|
||||
|
||||
public void Push( string value ) { Push( new JSONValue( value ) ); }
|
||||
public void Push( double value ) { Push( new JSONValue( value ) ); }
|
||||
public void Push( bool value ) { Push( new JSONValue( value ) ); }
|
||||
|
||||
public void Set( int index, JSONData data )
|
||||
{
|
||||
while ( index >= _values.Count )
|
||||
{
|
||||
Push( new JSONValue( null ) );
|
||||
}
|
||||
_values[ index ] = data;
|
||||
}
|
||||
|
||||
public void Set( int index, string value ) { Set( index, new JSONValue( value ) ); }
|
||||
public void Set( int index, bool value ) { Set( index, new JSONValue( value ) ); }
|
||||
public void Set( int index, double value ) { Set( index, new JSONValue( value ) ); }
|
||||
|
||||
public JSONData this[ int index ]
|
||||
{
|
||||
get { return Get( index ); }
|
||||
set { Set( index, value ); }
|
||||
}
|
||||
|
||||
public JSONData Pop()
|
||||
{
|
||||
return Lists.Pop( _values );
|
||||
}
|
||||
|
||||
public override string Stringify( StringifyOptions options )
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
if ( values.Count == 0)
|
||||
{
|
||||
options.increaseIndent();
|
||||
builder.Append( "[]" );
|
||||
options.decreaseIndent();
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
options.increaseIndent();
|
||||
builder.Append( "[\n" );
|
||||
if ( values.Count > 0 )
|
||||
{
|
||||
builder.Append( options.indent );
|
||||
builder.Append( values[ 0 ].Stringify( options ) );
|
||||
}
|
||||
|
||||
for ( var i = 1; i < values.Count; i++ )
|
||||
{
|
||||
builder.Append( ",\n" );
|
||||
builder.Append( options.indent );
|
||||
builder.Append( values[ i ].Stringify( options ) );
|
||||
}
|
||||
|
||||
options.decreaseIndent();
|
||||
builder.Append( "\n" );
|
||||
builder.Append( options.indent );
|
||||
builder.Append( "]" );
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public enum JSONDataType
|
||||
{
|
||||
BOOLEAN,
|
||||
NUMBER,
|
||||
STRING,
|
||||
OBJECT,
|
||||
ARRAY,
|
||||
NULL
|
||||
}
|
||||
|
||||
public class StringifyOptions
|
||||
{
|
||||
public bool pretty = true;
|
||||
public string indentString = " ";
|
||||
|
||||
int _indentLevel = -1;
|
||||
string _indent = "";
|
||||
|
||||
public void increaseIndent(){ _indentLevel++; UpdateIndent(); }
|
||||
public void decreaseIndent(){ _indentLevel--; UpdateIndent(); }
|
||||
|
||||
public string indent{ get { return _indent; }}
|
||||
|
||||
void UpdateIndent()
|
||||
{
|
||||
_indent = "";
|
||||
|
||||
for ( var i = 0; i < _indentLevel; i++ )
|
||||
{
|
||||
_indent += indentString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class JSONData
|
||||
{
|
||||
public abstract JSONDataType dataType { get; }
|
||||
public abstract string Stringify( StringifyOptions options );
|
||||
|
||||
public string Stringify()
|
||||
{
|
||||
return Stringify( new StringifyOptions() );
|
||||
}
|
||||
|
||||
public bool isArray
|
||||
{
|
||||
get { return dataType == JSONDataType.ARRAY; }
|
||||
}
|
||||
|
||||
public bool isObject
|
||||
{
|
||||
get { return dataType == JSONDataType.OBJECT; }
|
||||
}
|
||||
|
||||
public bool isNull
|
||||
{
|
||||
get { return dataType == JSONDataType.NULL; }
|
||||
}
|
||||
|
||||
public bool isNotNull
|
||||
{
|
||||
get { return dataType != JSONDataType.NULL; }
|
||||
}
|
||||
|
||||
public bool isNumber
|
||||
{
|
||||
get { return dataType == JSONDataType.NUMBER; }
|
||||
}
|
||||
|
||||
public bool isBoolean
|
||||
{
|
||||
get { return dataType == JSONDataType.BOOLEAN; }
|
||||
}
|
||||
|
||||
public bool isString
|
||||
{
|
||||
get { return dataType == JSONDataType.STRING; }
|
||||
}
|
||||
|
||||
public JSONArray AsArray()
|
||||
{
|
||||
if ( isArray )
|
||||
{
|
||||
return (JSONArray)this;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public JSONObject AsObject()
|
||||
{
|
||||
if ( isObject )
|
||||
{
|
||||
return (JSONObject)this;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static int GetInt( JSONData data, int alternative = 0 )
|
||||
{
|
||||
if ( data == null || ! data.isNumber )
|
||||
{
|
||||
return alternative;
|
||||
}
|
||||
|
||||
return data.intValue;
|
||||
}
|
||||
|
||||
public static float GetFloat( JSONData data, float alternative = 0 )
|
||||
{
|
||||
if ( data == null || ! data.isNumber )
|
||||
{
|
||||
return alternative;
|
||||
}
|
||||
|
||||
return data.floatValue;
|
||||
}
|
||||
|
||||
public static double GetNumber( JSONData data, double alternative = 0 )
|
||||
{
|
||||
if ( data == null || ! data.isNumber )
|
||||
{
|
||||
return alternative;
|
||||
}
|
||||
|
||||
return data.numberValue;
|
||||
}
|
||||
|
||||
public static bool GetBool( JSONData data, bool alternative = false )
|
||||
{
|
||||
if ( data == null || ! data.isBoolean )
|
||||
{
|
||||
return alternative;
|
||||
}
|
||||
|
||||
return data.booleanValue;
|
||||
}
|
||||
|
||||
public static string GetString( JSONData data, string alternative = "" )
|
||||
{
|
||||
if ( data == null || ! data.isString )
|
||||
{
|
||||
return alternative;
|
||||
}
|
||||
|
||||
return data.stringValue;
|
||||
}
|
||||
|
||||
public double numberValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = (JSONValue) this;
|
||||
return value.GetNumberValue();
|
||||
}
|
||||
}
|
||||
|
||||
public float floatValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = (JSONValue) this;
|
||||
return value.GetFloatValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int intValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = (JSONValue) this;
|
||||
return value.GetIntValue();
|
||||
}
|
||||
}
|
||||
|
||||
public bool booleanValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = (JSONValue) this;
|
||||
return value.GetBooleanValue();
|
||||
}
|
||||
}
|
||||
|
||||
public string stringValue
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = (JSONValue) this;
|
||||
return value.GetStringValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public object enumValue( System.Type enumType )
|
||||
{
|
||||
var value = intValue;
|
||||
try
|
||||
{
|
||||
var enumValue = System.Enum.ToObject( enumType , intValue );
|
||||
return enumValue;
|
||||
}
|
||||
catch ( System.Exception e )
|
||||
{
|
||||
RJLog.Log(
|
||||
"Exception:", e, "\n",
|
||||
"Couldn't parse enum value", value ,
|
||||
"JSON Data Type", dataType,
|
||||
"Stringified Value:", JSON.Stringify( this )
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,340 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
|
||||
public enum JSONLexerEventType
|
||||
{
|
||||
DONE_SUCCESS,
|
||||
DONE_ERROR,
|
||||
|
||||
OBJECT_START,
|
||||
OBJECT_END,
|
||||
ARRAY_START,
|
||||
ARRAY_END,
|
||||
IDENTIFIER,
|
||||
STRING,
|
||||
NUMBER,
|
||||
TRUE,
|
||||
FALSE,
|
||||
NULL,
|
||||
ARRAY_SEPERATOR,
|
||||
IDENTIFIER_SEPERATOR,
|
||||
|
||||
|
||||
ERROR_UNEXPECTED_SYMBOL,
|
||||
ERROR_UNCLOSED_STRING,
|
||||
ERROR_UNCLOSED_OBJECT,
|
||||
ERROR_UNCLOSED_ARRAY,
|
||||
ERROR_EXPECTED_FALSE,
|
||||
ERROR_EXPECTED_TRUE,
|
||||
ERROR_EXPECTED_NULL,
|
||||
ERROR_INFINITY_PREVENTION
|
||||
}
|
||||
|
||||
public class JSONLexer
|
||||
{
|
||||
public static bool IsErrorType( JSONLexerEventType type )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case JSONLexerEventType.ERROR_UNEXPECTED_SYMBOL:
|
||||
case JSONLexerEventType.ERROR_UNCLOSED_STRING:
|
||||
case JSONLexerEventType.ERROR_UNCLOSED_OBJECT:
|
||||
case JSONLexerEventType.ERROR_UNCLOSED_ARRAY:
|
||||
case JSONLexerEventType.ERROR_EXPECTED_FALSE:
|
||||
case JSONLexerEventType.ERROR_EXPECTED_TRUE:
|
||||
case JSONLexerEventType.ERROR_EXPECTED_NULL:
|
||||
case JSONLexerEventType.ERROR_INFINITY_PREVENTION:
|
||||
case JSONLexerEventType.DONE_ERROR:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public delegate void OnParse( JSONLexerEventType type, int offset, int length );
|
||||
|
||||
string source;
|
||||
OnParse onParseCallback;
|
||||
|
||||
|
||||
|
||||
public void Lex( string source, OnParse onParseCallback = null )
|
||||
{
|
||||
this.source = source;
|
||||
this.onParseCallback = onParseCallback;
|
||||
|
||||
Call( LexJSON(), 0 );
|
||||
|
||||
}
|
||||
|
||||
void Call( JSONLexerEventType type, int offset, int length = -1 )
|
||||
{
|
||||
if ( onParseCallback != null )
|
||||
{
|
||||
onParseCallback( type, offset, length );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
JSONLexerEventType LexJSON()
|
||||
{
|
||||
var offset = SkipWhiteSpace( 0 );
|
||||
|
||||
while ( offset < source.Length )
|
||||
{
|
||||
var offsetStart = offset;
|
||||
|
||||
var character = source[ offset ];
|
||||
|
||||
if ( character == '{' )
|
||||
{
|
||||
Call( JSONLexerEventType.OBJECT_START, offset , 1 );
|
||||
}
|
||||
else if ( character == '}' )
|
||||
{
|
||||
Call( JSONLexerEventType.OBJECT_END, offset , 1 );
|
||||
}
|
||||
else if ( character == '[' )
|
||||
{
|
||||
Call( JSONLexerEventType.ARRAY_START, offset , 1 );
|
||||
}
|
||||
else if ( character == ']' )
|
||||
{
|
||||
Call( JSONLexerEventType.ARRAY_END, offset , 1 );
|
||||
}
|
||||
else if ( character == ',' )
|
||||
{
|
||||
Call( JSONLexerEventType.ARRAY_SEPERATOR, offset , 1 );
|
||||
}
|
||||
else if ( character == ':' )
|
||||
{
|
||||
Call( JSONLexerEventType.IDENTIFIER_SEPERATOR, offset , 1 );
|
||||
}
|
||||
else if ( character == '\"' )
|
||||
{
|
||||
offset = LexString( offset );
|
||||
}
|
||||
else if ( character == 't' )
|
||||
{
|
||||
offset = LexTrue( offset );
|
||||
}
|
||||
else if ( character == 'f' )
|
||||
{
|
||||
offset = LexFalse( offset );
|
||||
}
|
||||
else if ( character == 'n' )
|
||||
{
|
||||
offset = LexNull( offset );
|
||||
}
|
||||
else if ( System.Char.IsDigit( character ) || character == '-' )
|
||||
{
|
||||
offset = LexNumber( offset );
|
||||
}
|
||||
|
||||
if ( offset == -1 )
|
||||
{
|
||||
return JSONLexerEventType.DONE_ERROR;
|
||||
}
|
||||
|
||||
offset ++;
|
||||
offset = SkipWhiteSpace( offset );
|
||||
|
||||
if ( offsetStart >= offset )
|
||||
{
|
||||
Call( JSONLexerEventType.ERROR_INFINITY_PREVENTION, offsetStart );
|
||||
return JSONLexerEventType.DONE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return JSONLexerEventType.DONE_SUCCESS;
|
||||
}
|
||||
|
||||
int SkipWhiteSpace( int offset )
|
||||
{
|
||||
|
||||
while ( offset < source.Length && System.Char.IsWhiteSpace( source[ offset ] ) )
|
||||
{
|
||||
offset ++;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool IsIdentifier( int offset )
|
||||
{
|
||||
offset = SkipWhiteSpace( offset );
|
||||
return offset < source.Length && source[ offset ] == ':';
|
||||
}
|
||||
|
||||
int LexString( int offset )
|
||||
{
|
||||
var offsetStart = offset;
|
||||
offset++;
|
||||
|
||||
var maxTries = 10000; var currentTries = 0;
|
||||
// currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
|
||||
|
||||
while ( offset < source.Length )
|
||||
{
|
||||
currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
|
||||
|
||||
var character = source[ offset ];
|
||||
|
||||
if ( character == '\"' )
|
||||
{
|
||||
var previousCharacter = source[ offset - 1 ];
|
||||
|
||||
if ( previousCharacter != '\\' )
|
||||
{
|
||||
var length = ( offset - offsetStart ) + 1;
|
||||
var isIdentifier = IsIdentifier( offset + 1 );
|
||||
var type = isIdentifier ? JSONLexerEventType.IDENTIFIER :
|
||||
JSONLexerEventType.STRING;
|
||||
Call( type, offsetStart, length );
|
||||
return ( offsetStart + length ) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
offset ++;
|
||||
|
||||
}
|
||||
|
||||
Call( JSONLexerEventType.ERROR_UNCLOSED_STRING, offsetStart );
|
||||
return -1;
|
||||
}
|
||||
|
||||
int LexTrue( int offset )
|
||||
{
|
||||
if ( MatchNextThree( offset, 'r', 'u', 'e' ) )
|
||||
{
|
||||
Call( JSONLexerEventType.TRUE, offset, 4 );
|
||||
return offset + 3;
|
||||
}
|
||||
|
||||
Call( JSONLexerEventType.ERROR_EXPECTED_TRUE, offset );
|
||||
return -1;
|
||||
}
|
||||
|
||||
int LexFalse( int offset )
|
||||
{
|
||||
if ( MatchNextFour( offset, 'a', 'l', 's', 'e' ) )
|
||||
{
|
||||
Call( JSONLexerEventType.FALSE, offset, 5 );
|
||||
return offset + 4;
|
||||
}
|
||||
Call( JSONLexerEventType.ERROR_EXPECTED_FALSE, offset );
|
||||
return -1;
|
||||
}
|
||||
|
||||
int LexNull( int offset )
|
||||
{
|
||||
if ( MatchNextThree( offset, 'u', 'l', 'l' ) )
|
||||
{
|
||||
Call( JSONLexerEventType.NULL, offset, 4 );
|
||||
return offset + 3;
|
||||
}
|
||||
|
||||
Call( JSONLexerEventType.ERROR_EXPECTED_NULL, offset );
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int LexNumber( int offset )
|
||||
{
|
||||
var hasDot = false;
|
||||
var hasE = false;
|
||||
var hasMinus = false;
|
||||
var parsing = true;
|
||||
|
||||
var offsetStart = offset;
|
||||
|
||||
var maxTries = 100; var currentTries = 0;
|
||||
// currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
|
||||
|
||||
while ( parsing && offset < source.Length )
|
||||
{
|
||||
currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
|
||||
var character = source[ offset ];
|
||||
|
||||
parsing = false;
|
||||
|
||||
if ( System.Char.IsDigit( character ) )
|
||||
{
|
||||
parsing = true;
|
||||
}
|
||||
else if ( character == '-' && ! hasMinus )
|
||||
{
|
||||
hasMinus = true;
|
||||
parsing = true;
|
||||
}
|
||||
else if ( character == '.' && ! hasDot )
|
||||
{
|
||||
hasDot = true;
|
||||
parsing = true;
|
||||
}
|
||||
else if ( ( character == 'e' || character == 'E' ) && ! hasE )
|
||||
{
|
||||
var hasNext = offset + 2 < source.Length;
|
||||
|
||||
if ( hasNext )
|
||||
{
|
||||
var nextCharacter = source[ offset + 1 ];
|
||||
hasE = nextCharacter == '+' || nextCharacter == '-';
|
||||
parsing = hasE;
|
||||
}
|
||||
|
||||
if ( parsing )
|
||||
{
|
||||
offset ++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( parsing )
|
||||
{
|
||||
offset ++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var length = ( offset - offsetStart );
|
||||
Call( JSONLexerEventType.NUMBER, offsetStart, length );
|
||||
|
||||
return ( offsetStart + length ) - 1;
|
||||
}
|
||||
|
||||
bool MatchNextThree( int offset, char next, char second, char third )
|
||||
{
|
||||
if ( offset + 3 >= source.Length ) { return false; }
|
||||
if ( source[ offset + 1 ] != next ) { return false; }
|
||||
if ( source[ offset + 2 ] != second ){ return false; }
|
||||
if ( source[ offset + 3 ] != third ) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MatchNextFour( int offset, char next, char second, char third, char fourth )
|
||||
{
|
||||
if ( offset + 4 >= source.Length ) { return false; }
|
||||
if ( source[ offset + 1 ] != next ) { return false; }
|
||||
if ( source[ offset + 2 ] != second ){ return false; }
|
||||
if ( source[ offset + 3 ] != third ) { return false; }
|
||||
if ( source[ offset + 4 ] != fourth ){ return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class JSONObject:JSONData
|
||||
{
|
||||
List<string> _keys = new List<string>();
|
||||
List<JSONData> _values = new List<JSONData>();
|
||||
|
||||
public override JSONDataType dataType
|
||||
{
|
||||
get { return JSONDataType.OBJECT; }
|
||||
}
|
||||
|
||||
public List<string> keys { get { return _keys; }}
|
||||
public List<JSONData> values { get { return _values; }}
|
||||
|
||||
public int size { get { return _keys.Count; } }
|
||||
|
||||
public JSONData Get( string key )
|
||||
{
|
||||
var index = _keys.IndexOf( key );
|
||||
return index == -1 ? null : _values[ index ];
|
||||
}
|
||||
|
||||
public string GetString( string key ){ return Get( key ).stringValue; }
|
||||
public double GetNumber( string key ){ return Get( key ).numberValue; }
|
||||
public int GetInt( string key ){ return Get( key ).intValue; }
|
||||
public float GetFloat( string key ){ return Get( key ).floatValue; }
|
||||
public bool GetBoolean( string key ){ return Get( key ).booleanValue; }
|
||||
|
||||
public T GetEnum<T>( string key )
|
||||
{
|
||||
var value = System.Enum.Parse( typeof( T ), GetString( key ) );
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
public double GetNumberOr( string key, double defaultFallback )
|
||||
{
|
||||
var index = _keys.IndexOf( key );
|
||||
|
||||
if ( index != -1 )
|
||||
{
|
||||
var value = _values[ index ];
|
||||
return value.isNumber ? value.numberValue : defaultFallback;
|
||||
}
|
||||
|
||||
return defaultFallback;
|
||||
}
|
||||
|
||||
public bool Contains( string key )
|
||||
{
|
||||
return _keys.Contains( key );
|
||||
}
|
||||
|
||||
public void Delete( string key )
|
||||
{
|
||||
var index = _keys.IndexOf( key );
|
||||
|
||||
if ( index != -1 )
|
||||
{
|
||||
_keys.RemoveAt( index );
|
||||
_values.RemoveAt( index );
|
||||
}
|
||||
}
|
||||
|
||||
public void Set( string key, JSONData value )
|
||||
{
|
||||
var index = _keys.IndexOf( key );
|
||||
|
||||
if ( index == -1 )
|
||||
{
|
||||
_keys.Add( key );
|
||||
_values.Add( value );
|
||||
}
|
||||
else
|
||||
{
|
||||
_values[ index ] = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Set( string key, double value ){ Set( key, new JSONValue( value ) ); }
|
||||
public void Set( string key, string value ){ Set( key, new JSONValue( value ) ); }
|
||||
public void Set( string key, bool value ){ Set( key, new JSONValue( value ) ); }
|
||||
public void SetEnum<T>( string key, object value)
|
||||
{
|
||||
var stringValue = System.Enum.GetName( typeof( T ), value );
|
||||
Set( key, stringValue );
|
||||
}
|
||||
|
||||
public override string Stringify( StringifyOptions options )
|
||||
{
|
||||
options.increaseIndent();
|
||||
var builder = new StringBuilder();
|
||||
|
||||
if ( values.Count == 0 )
|
||||
{
|
||||
builder.Append( "{}" );
|
||||
options.decreaseIndent();
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
|
||||
builder.Append( "{\n" );
|
||||
|
||||
if ( values.Count > 0 )
|
||||
{
|
||||
builder.Append( options.indent );
|
||||
JSONStringConverter.Write( builder, keys[ 0 ] );
|
||||
builder.Append( ": " );
|
||||
builder.Append( values[ 0 ].Stringify( options ) );
|
||||
}
|
||||
|
||||
for ( var i = 1; i < values.Count; i++ )
|
||||
{
|
||||
builder.Append( ",\n" );
|
||||
builder.Append( options.indent );
|
||||
JSONStringConverter.Write( builder, keys[ i ] );
|
||||
builder.Append( ": " );
|
||||
builder.Append( values[ i ].Stringify( options ) );
|
||||
}
|
||||
|
||||
options.decreaseIndent();
|
||||
builder.Append( "\n" );
|
||||
builder.Append( options.indent );
|
||||
builder.Append( "}" );
|
||||
|
||||
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
using LexerType = JSONLexerEventType;
|
||||
|
||||
public class JSONParser
|
||||
{
|
||||
List<JSONData> stack = new List<JSONData>();
|
||||
|
||||
JSONArray currentArray = null;
|
||||
JSONObject currentObject = null;
|
||||
JSONData current = null;
|
||||
bool currentIsArray = false;
|
||||
JSONData root = null;
|
||||
|
||||
string identifier = null;
|
||||
string source = null;
|
||||
bool hasError = false;
|
||||
JSONObject errorReport = null;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
stack = new List<JSONData>();
|
||||
|
||||
currentArray = null;
|
||||
currentObject = null;
|
||||
current = null;
|
||||
currentIsArray = false;
|
||||
root = null;
|
||||
|
||||
identifier = null;
|
||||
source = null;
|
||||
}
|
||||
|
||||
void ProcessJSONData( JSONData jsonData )
|
||||
{
|
||||
if ( current == null )
|
||||
{
|
||||
root = jsonData;
|
||||
}
|
||||
else if ( currentIsArray )
|
||||
{
|
||||
currentArray.Push( jsonData );
|
||||
}
|
||||
else
|
||||
{
|
||||
currentObject.Set( identifier, jsonData );
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SHOW_DEBUG_INFO = false;
|
||||
|
||||
void OnParse( LexerType type, int offset, int length )
|
||||
{
|
||||
if ( hasError ) { return; }
|
||||
|
||||
if ( SHOW_DEBUG_INFO )
|
||||
{
|
||||
RJLog.Log(type, offset, length );
|
||||
|
||||
if ( offset < source.Length && length > 0 && ( offset + length ) < source.Length )
|
||||
{
|
||||
RJLog.Log("'" + source.Substring( offset, length ) + "'" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch ( type )
|
||||
{
|
||||
case LexerType.NUMBER:
|
||||
{
|
||||
var stringValue = source.Substring( offset, length );
|
||||
//double numberValue = 0;
|
||||
double numberValue = RegexUtility.ParseDouble( stringValue );
|
||||
//System.Double.TryParse( stringValue, out numberValue );
|
||||
ProcessJSONData( new JSONValue( numberValue ) );
|
||||
}
|
||||
break;
|
||||
|
||||
case LexerType.STRING:
|
||||
{
|
||||
var stringValue = new StringBuilder();
|
||||
JSONStringConverter.Read( stringValue, source, offset, length );
|
||||
ProcessJSONData( new JSONValue( stringValue.ToString() ) );
|
||||
}
|
||||
break;
|
||||
|
||||
case LexerType.IDENTIFIER:
|
||||
{
|
||||
var stringValue = new StringBuilder();
|
||||
JSONStringConverter.Read( stringValue, source, offset, length );
|
||||
identifier = stringValue.ToString();
|
||||
}
|
||||
break;
|
||||
|
||||
case LexerType.NULL:
|
||||
case LexerType.TRUE:
|
||||
case LexerType.FALSE:
|
||||
{
|
||||
JSONData jsonData = new JSONValue();
|
||||
|
||||
if ( LexerType.NULL != type )
|
||||
{ jsonData = new JSONValue( LexerType.TRUE == type ); }
|
||||
|
||||
ProcessJSONData( jsonData );
|
||||
}
|
||||
break;
|
||||
|
||||
case LexerType.ARRAY_START:
|
||||
case LexerType.OBJECT_START:
|
||||
{
|
||||
var isArrayStart = LexerType.ARRAY_START == type;
|
||||
|
||||
JSONData jsonData = null;
|
||||
if ( isArrayStart ){ jsonData = new JSONArray(); }
|
||||
else { jsonData = new JSONObject(); }
|
||||
|
||||
ProcessJSONData( jsonData );
|
||||
|
||||
current = jsonData;
|
||||
|
||||
if ( isArrayStart )
|
||||
{
|
||||
currentArray = (JSONArray) jsonData;
|
||||
currentObject = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentObject = (JSONObject) jsonData;
|
||||
currentArray = null;
|
||||
}
|
||||
|
||||
stack.Add( current );
|
||||
currentIsArray = isArrayStart;
|
||||
}
|
||||
break;
|
||||
|
||||
case LexerType.ARRAY_END:
|
||||
case LexerType.OBJECT_END:
|
||||
{
|
||||
Lists.Pop( stack );
|
||||
current = Lists.Last( stack );
|
||||
|
||||
if ( current is JSONArray )
|
||||
{
|
||||
currentArray = (JSONArray) current;
|
||||
currentObject = null;
|
||||
currentIsArray = true;
|
||||
}
|
||||
else if ( current is JSONObject )
|
||||
{
|
||||
currentArray = null;
|
||||
currentObject = (JSONObject) current;
|
||||
currentIsArray = false;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case LexerType.ARRAY_SEPERATOR:
|
||||
case LexerType.IDENTIFIER_SEPERATOR:
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case LexerType.DONE_SUCCESS:
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// ERRORS:
|
||||
default:
|
||||
{
|
||||
hasError = true;
|
||||
CreateErrorReport( type, offset, length );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateErrorReport( LexerType type, int offset, int length )
|
||||
{
|
||||
var linesMapper = new TextLinesMapper();
|
||||
linesMapper.Map( source );
|
||||
errorReport = new JSONObject();
|
||||
|
||||
var end = offset + Mathf.Max( length, 0 );
|
||||
var linesInfo = linesMapper.GetTextEditorInfo( offset, end );
|
||||
var snippet = linesMapper.GetTextEditorSnippet( source, offset, end );
|
||||
|
||||
errorReport.Set( "errorType", type + "" );
|
||||
errorReport.Set( "linesInfo", linesInfo );
|
||||
errorReport.Set( "snippet", snippet );
|
||||
|
||||
}
|
||||
|
||||
public JSONData Parse( string source )
|
||||
{
|
||||
Reset();
|
||||
|
||||
this.source = source;
|
||||
|
||||
var lexer = new JSONLexer();
|
||||
lexer.Lex( source, this.OnParse );
|
||||
|
||||
if ( hasError )
|
||||
{
|
||||
RJLog.Log( errorReport.Stringify() );
|
||||
return null;
|
||||
}
|
||||
|
||||
return root;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class JSONStringConverter
|
||||
{
|
||||
static bool ContainsBackslash( string dataFromFile, int start, int length )
|
||||
{
|
||||
var end = start + length;
|
||||
for ( var i = start; i < end; i++ )
|
||||
{
|
||||
if ( dataFromFile[ i ] == '\\' ) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string Read( string dataFromFile )
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
Read( output, dataFromFile, 0, dataFromFile.Length );
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
public static void Read( StringBuilder output, string dataFromFile, int start, int length )
|
||||
{
|
||||
ReadWithoutQuotes( output, dataFromFile, start + 1, length - 2 );
|
||||
}
|
||||
|
||||
public static void Read( StringBuilder output, string dataFromFile )
|
||||
{
|
||||
Read( output, dataFromFile, 0, dataFromFile.Length );
|
||||
}
|
||||
|
||||
public static void ReadWithoutQuotes( StringBuilder output, string dataFromFile, int start, int length )
|
||||
{
|
||||
if ( ! JSONStringConverter.ContainsBackslash( dataFromFile, start, length ) )
|
||||
{
|
||||
output.Append( dataFromFile.Substring( start, length ) );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var end = start + length;
|
||||
|
||||
for ( var i = start ; i < end; i++ )
|
||||
{
|
||||
var rawCharacter = dataFromFile[ i ];
|
||||
|
||||
if ( rawCharacter == '\\' )
|
||||
{
|
||||
if ( i == end - 1 ) { return; }
|
||||
|
||||
var nextRawCharacter = dataFromFile[ i + 1 ];
|
||||
|
||||
if ( nextRawCharacter == 'u' )
|
||||
{
|
||||
var hexStringStart = i + 2;
|
||||
var hexString = dataFromFile.Substring( hexStringStart, 4 );
|
||||
var unicodeValue = ReadUnicodeFromHexString( hexString );
|
||||
|
||||
output.Append( unicodeValue );
|
||||
|
||||
i += 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
var escapedCharacter = ReadSingleEscaped( nextRawCharacter );
|
||||
output.Append( escapedCharacter );
|
||||
|
||||
i ++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Append( rawCharacter );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static char ReadSingleEscaped( char character )
|
||||
{
|
||||
switch ( character )
|
||||
{
|
||||
case 'b': return '\b';
|
||||
case 'f': return '\f';
|
||||
case 'n': return '\n';
|
||||
case 'r': return '\r';
|
||||
case 't': return '\t';
|
||||
|
||||
default: return character;
|
||||
}
|
||||
}
|
||||
|
||||
static char ReadUnicodeFromHexString( string hexString )
|
||||
{
|
||||
return (char) int.Parse( hexString, System.Globalization.NumberStyles.HexNumber );
|
||||
}
|
||||
|
||||
static readonly char[] ESCAPE_CHARACTERS = new char[]
|
||||
{
|
||||
'\"', '\\', '/', '\b', '\f', '\n', '\r', '\t'
|
||||
};
|
||||
|
||||
static readonly string[] ESCAPING_SEQUENCES = new string[]
|
||||
{
|
||||
"\\\"", "\\\\", "\\/", "\\b", "\\f", "\\n", "\\r", "\\t"
|
||||
};
|
||||
|
||||
public static string Write( string dataFromMemory )
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
Write( output, dataFromMemory, 0, dataFromMemory.Length );
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
public static void Write( StringBuilder output, string dataFromMemory, int start, int length )
|
||||
{
|
||||
output.Append( "\"" );
|
||||
WriteWithoutQuotes( output, dataFromMemory, start, length );
|
||||
output.Append( "\"" );
|
||||
}
|
||||
|
||||
public static void Write( StringBuilder output, string dataFromMemory )
|
||||
{
|
||||
Write( output, dataFromMemory, 0, dataFromMemory.Length );
|
||||
}
|
||||
|
||||
|
||||
public static void WriteWithoutQuotes( StringBuilder output, string dataFromMemory, int start, int length )
|
||||
{
|
||||
var end = start + length;
|
||||
var index = IndexOfEscapeCharacter( dataFromMemory, start, end );
|
||||
|
||||
if ( index == -1 )
|
||||
{
|
||||
output.Append( dataFromMemory.Substring( start, length ) );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for ( var i = 0; i < end; i++ )
|
||||
{
|
||||
var intValue = ( int ) dataFromMemory[ i ];
|
||||
|
||||
if ( intValue > 127 )
|
||||
{
|
||||
var escapedUnicodeValue = "\\u" + intValue.ToString( "x4" );
|
||||
output.Append( escapedUnicodeValue );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var escapeIndex = GetEscapeIndex( dataFromMemory[ i ] );
|
||||
|
||||
if ( escapeIndex != -1 )
|
||||
{
|
||||
output.Append( ESCAPING_SEQUENCES[ escapeIndex ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Append( dataFromMemory[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int IndexOfEscapeCharacter( string dataFromMemory, int start, int end )
|
||||
{
|
||||
for ( var i = start; i < end; i++ )
|
||||
{
|
||||
var intValue = ( int ) dataFromMemory[ i ];
|
||||
|
||||
if ( intValue > 127 )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
var escapeIndex = GetEscapeIndex( dataFromMemory[ i ] );
|
||||
|
||||
if ( escapeIndex != -1 )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int GetEscapeIndex( char c )
|
||||
{
|
||||
for ( int i = 0; i < ESCAPE_CHARACTERS.Length; i++ )
|
||||
{
|
||||
if ( ESCAPE_CHARACTERS[ i ] == c )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class JSONValue:JSONData
|
||||
{
|
||||
JSONDataType _dataType;
|
||||
double _numberValue = 0;
|
||||
bool _booleanValue = false;
|
||||
string _stringValue = "";
|
||||
|
||||
public override JSONDataType dataType
|
||||
{
|
||||
get { return _dataType; }
|
||||
}
|
||||
|
||||
public JSONValue(){ SetNull(); }
|
||||
public JSONValue( string value ){ Set( value ); }
|
||||
public JSONValue( double value ){ Set( value ); }
|
||||
public JSONValue( bool value ){ Set( value ); }
|
||||
|
||||
|
||||
private void Set( double number )
|
||||
{
|
||||
_numberValue = number;
|
||||
_dataType = JSONDataType.NUMBER;
|
||||
}
|
||||
|
||||
private void Set( string value )
|
||||
{
|
||||
_stringValue = value;
|
||||
_dataType = value == null ? JSONDataType.NULL : JSONDataType.STRING;
|
||||
}
|
||||
|
||||
private void Set( bool flag )
|
||||
{
|
||||
_booleanValue = flag;
|
||||
_dataType = JSONDataType.BOOLEAN;
|
||||
}
|
||||
|
||||
private void SetNull()
|
||||
{
|
||||
_dataType = JSONDataType.NULL;
|
||||
}
|
||||
|
||||
public double GetNumberValue(){ return _numberValue; }
|
||||
public float GetFloatValue() { return (float) _numberValue; }
|
||||
public int GetIntValue(){ return (int) System.Math.Round( _numberValue ); }
|
||||
public bool GetBooleanValue(){ return _booleanValue; }
|
||||
public string GetStringValue(){ return _stringValue; }
|
||||
public T GetEnumFromString<T>( T enumValue ) where T : System.Enum
|
||||
{
|
||||
//System.Enum.TryParse( _stringValue, out T enumValue);
|
||||
return enumValue;
|
||||
}
|
||||
|
||||
public override string Stringify( StringifyOptions options )
|
||||
{
|
||||
if ( JSONDataType.STRING == _dataType )
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
JSONStringConverter.Write( output, _stringValue, 0, _stringValue.Length );
|
||||
return output.ToString();
|
||||
}
|
||||
else if ( JSONDataType.NUMBER == _dataType )
|
||||
{
|
||||
return RegexUtility.WriteDouble( _numberValue );
|
||||
}
|
||||
else if ( JSONDataType.BOOLEAN == _dataType )
|
||||
{
|
||||
return _booleanValue ? "true" : "false";
|
||||
}
|
||||
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class BigIntegerSerializer: CustomSerializer
|
||||
{
|
||||
public override List<Type> HandlingTypes()
|
||||
{
|
||||
return new List<Type>(){ typeof( BigInteger ) };
|
||||
}
|
||||
|
||||
|
||||
public override JSONData Serialize( object value )
|
||||
{
|
||||
var bigInteger = (BigInteger) value;
|
||||
return new JSONValue( bigInteger.ToString( "R" ) );
|
||||
}
|
||||
|
||||
public override void Deserialize( JSONData data, Reference referenceRelation )
|
||||
{
|
||||
if ( referenceRelation.target == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var bigInteger = BigInteger.Parse( data.stringValue );
|
||||
|
||||
referenceRelation.AssignValue( bigInteger );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System;
|
||||
|
||||
using Godot;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class ColorSerializer: CustomSerializer
|
||||
{
|
||||
public override List<Type> HandlingTypes()
|
||||
{
|
||||
return new List<Type>(){ typeof( Color ) };
|
||||
}
|
||||
|
||||
|
||||
public override JSONData Serialize( object value )
|
||||
{
|
||||
var color = (Color) value;
|
||||
var jsonArray = new JSONArray();
|
||||
jsonArray.Push( color.R );
|
||||
jsonArray.Push( color.G );
|
||||
jsonArray.Push( color.B );
|
||||
jsonArray.Push( color.A );
|
||||
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
public override void Deserialize( JSONData data, Reference referenceRelation )
|
||||
{
|
||||
if ( referenceRelation.target == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
var array = data.AsArray();
|
||||
|
||||
var color = new Color(
|
||||
array.Get( 0 ).floatValue,
|
||||
array.Get( 1 ).floatValue,
|
||||
array.Get( 2 ).floatValue,
|
||||
array.Get( 3 ).floatValue
|
||||
);
|
||||
|
||||
referenceRelation.AssignValue( color );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public abstract class CustomSerializer
|
||||
{
|
||||
public abstract List<Type> HandlingTypes();
|
||||
public abstract JSONData Serialize( object value );
|
||||
public abstract void Deserialize( JSONData data, Reference reference );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class DateTimeSerializer: CustomSerializer
|
||||
{
|
||||
public override List<Type> HandlingTypes()
|
||||
{
|
||||
return new List<Type>(){ typeof( DateTime ) };
|
||||
}
|
||||
|
||||
|
||||
public override JSONData Serialize( object value )
|
||||
{
|
||||
var dateTime = (DateTime) value;
|
||||
return new JSONValue( dateTime.ToString( "o" ) );
|
||||
}
|
||||
|
||||
public override void Deserialize( JSONData data, Reference referenceRelation )
|
||||
{
|
||||
if ( referenceRelation.target == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dateTime = System.DateTime.Parse( data.stringValue,
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.RoundtripKind );
|
||||
|
||||
referenceRelation.AssignValue( dateTime );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
using System.Numerics;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class JSONAlwaysProcessable: System.Attribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,397 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class JSONDeserializer
|
||||
{
|
||||
JSONSerializationSettings settings = new JSONSerializationSettings();
|
||||
Dictionary<Type,CustomSerializer> customSerializers = null;
|
||||
|
||||
|
||||
public JSONDeserializer ( JSONSerializationSettings settings )
|
||||
{
|
||||
this.settings = settings == null ? new JSONSerializationSettings() : settings;
|
||||
}
|
||||
|
||||
public T Deserialize<T>( string data ) where T:new()
|
||||
{
|
||||
return Deserialize<T>( JSON.Parse( data ).AsObject() );
|
||||
}
|
||||
|
||||
public T Deserialize<T>( JSONObject jsonObject ) where T:new()
|
||||
{
|
||||
Initialize();
|
||||
|
||||
var output = new T();
|
||||
var keys = jsonObject.keys;
|
||||
|
||||
for ( int i = 0; i < keys.Count; i++)
|
||||
{
|
||||
var key = keys[ i ];
|
||||
DeserializeMemberField( jsonObject.Get( key ), output, key );
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public void Deserialize<T>( string data, T output )
|
||||
{
|
||||
Deserialize<T>( JSON.Parse( data ).AsObject(), output );
|
||||
}
|
||||
|
||||
public void Deserialize<T>( JSONObject jsonObject, T output )
|
||||
{
|
||||
Initialize();
|
||||
|
||||
var keys = jsonObject.keys;
|
||||
|
||||
for ( int i = 0; i < keys.Count; i++)
|
||||
{
|
||||
var key = keys[ i ];
|
||||
DeserializeMemberField( jsonObject.Get( key ), output, key );
|
||||
}
|
||||
}
|
||||
|
||||
void DeserializeMemberField( JSONData data, object parent, string name )
|
||||
{
|
||||
if ( data.isNull )
|
||||
{
|
||||
SetMemberValue( parent, name, null );
|
||||
return;
|
||||
}
|
||||
|
||||
var type = parent.GetType();
|
||||
var field = type.GetField( name );
|
||||
|
||||
if ( field == null )
|
||||
{
|
||||
RJLog.Log( "Field not present: " + name );
|
||||
return;
|
||||
}
|
||||
|
||||
var reference = new Reference( parent, name );
|
||||
var fieldType = field.FieldType;
|
||||
|
||||
AssignValue( data, reference, fieldType );
|
||||
|
||||
}
|
||||
|
||||
void DeserializeListField( JSONData data, IList parent, int index )
|
||||
{
|
||||
if ( data.isNull )
|
||||
{
|
||||
SetListValue( parent, index, null );
|
||||
return;
|
||||
}
|
||||
|
||||
var type = parent.GetType();
|
||||
var reference = new Reference( parent, index );
|
||||
var listType = type.GetGenericArguments()[ 0 ];
|
||||
|
||||
AssignValue( data, reference, listType );
|
||||
}
|
||||
|
||||
void DeserializeDictionaryField( JSONData data, IDictionary parent, string key, int index )
|
||||
{
|
||||
var type = parent.GetType();
|
||||
var isIndexed = type.GetGenericArguments()[ 0 ] == typeof( int );
|
||||
|
||||
if ( data.isNull )
|
||||
{
|
||||
if ( isIndexed )
|
||||
{
|
||||
SetDictionaryValue( parent, index, null );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDictionaryValue( parent, key, null );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var reference = isIndexed ? new Reference( parent, index ) : new Reference( parent, key );
|
||||
var valueType = type.GetGenericArguments()[ 1 ];
|
||||
|
||||
AssignValue( data, reference, valueType );
|
||||
}
|
||||
|
||||
void AssignValue( JSONData data, Reference reference, Type type )
|
||||
{
|
||||
var customSerializer = GetCustomDeserializer( type );
|
||||
|
||||
if ( customSerializer != null )
|
||||
{
|
||||
customSerializer.Deserialize( data, reference );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( typeof( int ) == type )
|
||||
{
|
||||
SetNumber( data, reference, INT );
|
||||
}
|
||||
else if ( typeof( float ) == type )
|
||||
{
|
||||
SetNumber( data, reference, FLOAT );
|
||||
}
|
||||
else if ( typeof( double ) == type )
|
||||
{
|
||||
SetNumber( data, reference, DOUBLE );
|
||||
}
|
||||
else if ( typeof( bool ) == type )
|
||||
{
|
||||
if ( ! data.isBoolean )
|
||||
{
|
||||
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
|
||||
return;
|
||||
}
|
||||
reference.AssignValue( data.booleanValue );
|
||||
}
|
||||
else if ( type.IsEnum )
|
||||
{
|
||||
reference.AssignValue( data.enumValue( type ) );
|
||||
}
|
||||
else if ( typeof( string ) == type )
|
||||
{
|
||||
if ( ! data.isString )
|
||||
{
|
||||
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
|
||||
return;
|
||||
}
|
||||
reference.AssignValue( data.stringValue );
|
||||
}
|
||||
else if ( ReflectionHelper.IsList( type ) )
|
||||
{
|
||||
if ( ! data.isArray )
|
||||
{
|
||||
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
|
||||
return;
|
||||
}
|
||||
|
||||
reference.AssignValue( CreateList( data, type ) );
|
||||
}
|
||||
else if ( IsSerializableDictionary( type ) )
|
||||
{
|
||||
if ( ! data.isObject )
|
||||
{
|
||||
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
|
||||
return;
|
||||
}
|
||||
|
||||
reference.AssignValue( CreateDictionary( data, type ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! data.isObject )
|
||||
{
|
||||
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
|
||||
return;
|
||||
}
|
||||
|
||||
reference.AssignValue( CreateObject( data, type ) );
|
||||
}
|
||||
}
|
||||
|
||||
IList CreateList( JSONData data, Type fieldType )
|
||||
{
|
||||
var array = data.AsArray();
|
||||
var list = (IList) Activator.CreateInstance( fieldType );
|
||||
|
||||
for ( int i = 0 ; i < array.size; i++ )
|
||||
{
|
||||
var jsonValue = array.Get( i );
|
||||
DeserializeListField( jsonValue, list, i );
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
IDictionary CreateDictionary( JSONData data, Type fieldType )
|
||||
{
|
||||
var map = data.AsObject();
|
||||
var dict = (IDictionary) Activator.CreateInstance( fieldType );
|
||||
var keys = map.keys;
|
||||
|
||||
var isIndexed = dict.GetType().GetGenericArguments()[ 0 ] == typeof( int );
|
||||
|
||||
if ( isIndexed )
|
||||
{
|
||||
for ( int i = 0 ; i < keys.Count; i++ )
|
||||
{
|
||||
var jsonValue = map.Get( keys[ i ] );
|
||||
var index = RegexUtility.ParseInt( keys[ i ] );
|
||||
DeserializeDictionaryField( jsonValue, dict, "", index );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i = 0 ; i < keys.Count; i++ )
|
||||
{
|
||||
var jsonValue = map.Get( keys[ i ] );
|
||||
DeserializeDictionaryField( jsonValue, dict, keys[ i ], 0 );
|
||||
}
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
object CreateObject( JSONData data, Type type )
|
||||
{
|
||||
var output = Activator.CreateInstance( type );
|
||||
var jsonObject = data.AsObject();
|
||||
var keys = jsonObject.keys;
|
||||
|
||||
for ( int i = 0; i < keys.Count; i++)
|
||||
{
|
||||
var key = keys[ i ];
|
||||
DeserializeMemberField( jsonObject.Get( key ), output, key );
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void SetMemberValue( object instance, string name, object value )
|
||||
{
|
||||
ReflectionHelper.SetMemberValue( instance, name, value );
|
||||
}
|
||||
|
||||
void SetListValue( IList list, int index, object value )
|
||||
{
|
||||
list[ index ] = value;
|
||||
}
|
||||
|
||||
void SetDictionaryValue( IDictionary dictionary, string index, object value )
|
||||
{
|
||||
dictionary[ index ] = value;
|
||||
}
|
||||
|
||||
void SetDictionaryValue( IDictionary dictionary, int index, object value )
|
||||
{
|
||||
dictionary[ index ] = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const int INT = 0;
|
||||
const int FLOAT = 1;
|
||||
const int DOUBLE = 2;
|
||||
|
||||
void SetNumber( JSONData data, Reference reference, int numberType )
|
||||
{
|
||||
if ( ! settings.saveNumbersWithType )
|
||||
{
|
||||
if ( ! data.isNumber )
|
||||
{
|
||||
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( numberType )
|
||||
{
|
||||
case INT:{ reference.AssignValue( data.intValue ); return; }
|
||||
case FLOAT:{ reference.AssignValue( data.floatValue ); return; }
|
||||
case DOUBLE:{ reference.AssignValue( data.numberValue ); return; }
|
||||
}
|
||||
|
||||
RJLog.Log( "Unknown number type: " + reference.GetInfo() + ">>" + data );
|
||||
return;
|
||||
|
||||
|
||||
}
|
||||
|
||||
if ( ! data.isObject )
|
||||
{
|
||||
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
|
||||
return;
|
||||
}
|
||||
|
||||
var objectValue = data.AsObject();
|
||||
|
||||
if ( ! objectValue.Contains( JSONSerializationSettings.NUMBER_VALUE ) )
|
||||
{
|
||||
RJLog.Log( "Type not matching: " + reference.GetInfo() + ">>" + data );
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( numberType )
|
||||
{
|
||||
case INT:
|
||||
{
|
||||
var value = objectValue.Get( JSONSerializationSettings.NUMBER_VALUE ).intValue;
|
||||
reference.AssignValue( value );
|
||||
}
|
||||
break;
|
||||
|
||||
case FLOAT:
|
||||
{
|
||||
var value = objectValue.Get( JSONSerializationSettings.NUMBER_VALUE ).floatValue;
|
||||
reference.AssignValue( value );
|
||||
}
|
||||
break;
|
||||
|
||||
case DOUBLE:
|
||||
{
|
||||
var value = objectValue.Get( JSONSerializationSettings.NUMBER_VALUE ).numberValue;
|
||||
reference.AssignValue( value );
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
if ( customSerializers != null ){ return ;}
|
||||
|
||||
customSerializers = new Dictionary<Type,CustomSerializer>();
|
||||
|
||||
settings.customSerializers.ForEach( s => AddSerializer( s ) );
|
||||
|
||||
JSONSerializationSettings.defaultSerializers.ForEach( s => AddSerializer( s ) );
|
||||
}
|
||||
|
||||
void AddSerializer( CustomSerializer serializer )
|
||||
{
|
||||
var types = serializer.HandlingTypes();
|
||||
types.ForEach( type => AddSerializerForType( type, serializer ) );
|
||||
|
||||
}
|
||||
|
||||
void AddSerializerForType( Type type, CustomSerializer serializer )
|
||||
{
|
||||
if ( customSerializers.ContainsKey( type ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
customSerializers[ type ] = serializer;
|
||||
}
|
||||
|
||||
|
||||
CustomSerializer GetCustomDeserializer( Type type )
|
||||
{
|
||||
|
||||
if ( customSerializers.ContainsKey( type ) )
|
||||
{
|
||||
return customSerializers[ type ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool IsSerializableDictionary( Type type )
|
||||
{
|
||||
return JSONSerializationSettings.IsSerializableDictionary( type );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,429 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using System.Numerics;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class JSONSerializer
|
||||
{
|
||||
JSONSerializationSettings settings = new JSONSerializationSettings();
|
||||
HashSet<object> processedObjects = new HashSet<object>();
|
||||
HashSet<Type> alwaysReprocessTypes = new HashSet<Type>()
|
||||
{
|
||||
typeof( DateTime ), typeof( BigInteger )
|
||||
};
|
||||
|
||||
public JSONSerializer ( JSONSerializationSettings settings = null )
|
||||
{
|
||||
this.settings = settings == null ? new JSONSerializationSettings() : settings;
|
||||
}
|
||||
|
||||
Dictionary<Type,CustomSerializer> customSerializers = null;
|
||||
|
||||
public string Serialize( object value )
|
||||
{
|
||||
|
||||
Initialize();
|
||||
|
||||
if ( value == null )
|
||||
{
|
||||
return JSON.Stringify( new JSONValue() );
|
||||
}
|
||||
else if ( value is int )
|
||||
{
|
||||
return JSON.Stringify( CreateNumber( (int) value ) );
|
||||
}
|
||||
else if ( value is double )
|
||||
{
|
||||
return JSON.Stringify( CreateNumber( (double) value ) );
|
||||
}
|
||||
else if ( value is float )
|
||||
{
|
||||
return JSON.Stringify( CreateNumber( (float) value ) );
|
||||
}
|
||||
else if ( value is string )
|
||||
{
|
||||
return JSON.Stringify( new JSONValue( (string) value ) );
|
||||
}
|
||||
else if ( value is bool )
|
||||
{
|
||||
return JSON.Stringify( new JSONValue( (bool) value ) );
|
||||
}
|
||||
else if ( ReflectionHelper.IsList( value ) )
|
||||
{
|
||||
return JSON.Stringify( CreateArray( value as IList ) );
|
||||
}
|
||||
else if ( IsSerializableDictionary( value ) )
|
||||
{
|
||||
return JSON.Stringify( CreateMapLikeObject( value as IDictionary ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
var serializer = GetCustomSerializer( value );
|
||||
|
||||
if ( serializer != null )
|
||||
{
|
||||
return JSON.Stringify( serializer.Serialize( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return JSON.Stringify( CreateObject( value ) );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
processedObjects.Clear();
|
||||
|
||||
if ( customSerializers != null ){ return ;}
|
||||
|
||||
customSerializers = new Dictionary<Type,CustomSerializer>();
|
||||
|
||||
settings.customSerializers.ForEach( s => AddSerializer( s ) );
|
||||
|
||||
JSONSerializationSettings.defaultSerializers.ForEach( s => AddSerializer( s ) );
|
||||
}
|
||||
|
||||
void AddSerializer( CustomSerializer serializer )
|
||||
{
|
||||
var types = serializer.HandlingTypes();
|
||||
types.ForEach( type => AddSerializerForType( type, serializer ) );
|
||||
|
||||
}
|
||||
|
||||
void AddSerializerForType( Type type, CustomSerializer serializer )
|
||||
{
|
||||
if ( customSerializers.ContainsKey( type ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
customSerializers[ type ] = serializer;
|
||||
}
|
||||
|
||||
|
||||
CustomSerializer GetCustomSerializer( object value )
|
||||
{
|
||||
if ( value == null ){ return null; }
|
||||
|
||||
var type = value.GetType();
|
||||
|
||||
if ( customSerializers.ContainsKey( type ) )
|
||||
{
|
||||
return customSerializers[ type ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool IsSerializableDictionary( object value )
|
||||
{
|
||||
return JSONSerializationSettings.IsSerializableDictionary( value );
|
||||
}
|
||||
|
||||
|
||||
|
||||
JSONData CreateNumber( int value )
|
||||
{
|
||||
if ( settings.saveNumbersWithType )
|
||||
{
|
||||
var jsonObject = new JSONObject();
|
||||
jsonObject.Set( JSONSerializationSettings.NUMBER_TYPE, JSONSerializationSettings.INT_NUMBER_TYPE );
|
||||
jsonObject.Set( JSONSerializationSettings.NUMBER_VALUE, value );
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
return new JSONValue( value );
|
||||
}
|
||||
|
||||
JSONData CreateNumber( float value )
|
||||
{
|
||||
if ( settings.saveNumbersWithType )
|
||||
{
|
||||
var jsonObject = new JSONObject();
|
||||
jsonObject.Set( JSONSerializationSettings.NUMBER_TYPE, JSONSerializationSettings.FLOAT_NUMBER_TYPE );
|
||||
jsonObject.Set( JSONSerializationSettings.NUMBER_VALUE, value );
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
return new JSONValue( value );
|
||||
}
|
||||
|
||||
JSONData CreateNumber( double value )
|
||||
{
|
||||
if ( settings.saveNumbersWithType )
|
||||
{
|
||||
var jsonObject = new JSONObject();
|
||||
jsonObject.Set( JSONSerializationSettings.NUMBER_TYPE, JSONSerializationSettings.DOUBLE_NUMBER_TYPE );
|
||||
jsonObject.Set( JSONSerializationSettings.NUMBER_VALUE, value );
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
return new JSONValue( value );
|
||||
}
|
||||
|
||||
bool IsProcessableObject( object value )
|
||||
{
|
||||
if (System.Attribute.IsDefined( value.GetType(), typeof(JSONAlwaysProcessable) ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( alwaysReprocessTypes.Contains( value.GetType() ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( processedObjects.Contains( value ) )
|
||||
{
|
||||
RJLog.Error( "Cycle detected: " + value );
|
||||
var interfaces = typeof( object ).GetInterfaces();
|
||||
|
||||
for ( int i = 0; i < interfaces.Length; i++ )
|
||||
{
|
||||
RJLog.Error( "Interfaces[" + i + "]" + interfaces[ i ].Name );
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ( settings.throwErrorOnCycles )
|
||||
{
|
||||
throw new System.Exception( "Cycle detected: " + value );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
processedObjects.Add( value );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JSONData CreateArray( IList value )
|
||||
{
|
||||
if ( ! IsProcessableObject( value ) )
|
||||
{
|
||||
return new JSONValue();
|
||||
}
|
||||
|
||||
var jsonArray = new JSONArray();
|
||||
|
||||
//RJLog.Log(value );
|
||||
|
||||
for ( int i = 0; i < value.Count; i++ )
|
||||
{
|
||||
AssignArrayMember( jsonArray, i, value[ i ] );
|
||||
}
|
||||
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
JSONData CreateMapLikeObject( IDictionary value )
|
||||
{
|
||||
if ( ! IsProcessableObject( value ) )
|
||||
{
|
||||
return new JSONValue();
|
||||
}
|
||||
|
||||
var jsonObject = new JSONObject();
|
||||
|
||||
foreach ( var key in value.Keys )
|
||||
{
|
||||
AssignObjectMember( jsonObject, key + "", value[ key ] );
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
JSONData CreateObject( object obj )
|
||||
{
|
||||
if ( ! IsProcessableObject( obj ) )
|
||||
{
|
||||
return new JSONValue();
|
||||
}
|
||||
|
||||
var type = obj.GetType();
|
||||
var fields = type.GetFields();
|
||||
var jsonObject = new JSONObject();
|
||||
|
||||
foreach ( var f in fields )
|
||||
{
|
||||
if ( f.IsStatic )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var name = f.Name;
|
||||
var value = f.GetValue( obj );
|
||||
|
||||
if ( value == null )
|
||||
{
|
||||
jsonObject.Set( name, new JSONValue() );
|
||||
}
|
||||
else if ( value is int )
|
||||
{
|
||||
jsonObject.Set( name, CreateNumber( (int) value ) );
|
||||
}
|
||||
else if ( value is double )
|
||||
{
|
||||
jsonObject.Set( name, CreateNumber( (double) value ) );
|
||||
}
|
||||
else if ( value is float )
|
||||
{
|
||||
jsonObject.Set( name, CreateNumber( (float) value ) );
|
||||
}
|
||||
else if ( value is string )
|
||||
{
|
||||
jsonObject.Set( name, (string) value );
|
||||
}
|
||||
else if ( value is bool )
|
||||
{
|
||||
jsonObject.Set( name, (bool) value );
|
||||
}
|
||||
else if ( value is Enum )
|
||||
{
|
||||
jsonObject.Set( name, (int) value );
|
||||
}
|
||||
else if ( ReflectionHelper.IsList( value ) )
|
||||
{
|
||||
jsonObject.Set( name, CreateArray( value as IList ) );
|
||||
}
|
||||
else if ( IsSerializableDictionary( value ) )
|
||||
{
|
||||
jsonObject.Set( name, CreateMapLikeObject( value as IDictionary ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
var exporter = GetCustomSerializer( value );
|
||||
|
||||
if ( exporter != null )
|
||||
{
|
||||
jsonObject.Set( name, exporter.Serialize( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonObject.Set( name, CreateObject( value ) );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
void AssignArrayMember( JSONArray jsonArray, int index, object value )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
jsonArray.Set( index, new JSONValue() );
|
||||
}
|
||||
else if ( value is int )
|
||||
{
|
||||
jsonArray.Set( index, CreateNumber( (int) value ) );
|
||||
}
|
||||
else if ( value is double )
|
||||
{
|
||||
jsonArray.Set( index, CreateNumber( (double) value ) );
|
||||
}
|
||||
else if ( value is float )
|
||||
{
|
||||
jsonArray.Set( index, CreateNumber( (float) value ) );
|
||||
}
|
||||
else if ( value is string )
|
||||
{
|
||||
jsonArray.Set( index, (string) value );
|
||||
}
|
||||
else if ( value is bool )
|
||||
{
|
||||
jsonArray.Set( index, (bool) value );
|
||||
}
|
||||
else if ( ReflectionHelper.IsList( value ) )
|
||||
{
|
||||
jsonArray.Set( index, CreateArray( value as IList) );
|
||||
}
|
||||
else if ( IsSerializableDictionary( value ) )
|
||||
{
|
||||
jsonArray.Set( index, CreateMapLikeObject( value as IDictionary ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
var exporter = GetCustomSerializer( value );
|
||||
|
||||
if ( exporter != null )
|
||||
{
|
||||
jsonArray.Set( index, exporter.Serialize( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonArray.Set( index, CreateObject( value ) );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void AssignObjectMember( JSONObject jsonObject, string name, object value )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
jsonObject.Set( name, new JSONValue() );
|
||||
}
|
||||
else if ( value is int )
|
||||
{
|
||||
jsonObject.Set( name, CreateNumber( (int) value ) );
|
||||
}
|
||||
else if ( value is double )
|
||||
{
|
||||
jsonObject.Set( name, CreateNumber( (double) value ) );
|
||||
}
|
||||
else if ( value is float )
|
||||
{
|
||||
jsonObject.Set( name, CreateNumber( (float) value ) );
|
||||
}
|
||||
else if ( value is string )
|
||||
{
|
||||
jsonObject.Set( name, (string) value );
|
||||
}
|
||||
else if ( value is bool )
|
||||
{
|
||||
jsonObject.Set( name, (bool) value );
|
||||
}
|
||||
else if ( ReflectionHelper.IsList( value ) )
|
||||
{
|
||||
jsonObject.Set( name, CreateArray( value as IList ) );
|
||||
}
|
||||
else if ( IsSerializableDictionary( value ) )
|
||||
{
|
||||
jsonObject.Set( name, CreateMapLikeObject( value as IDictionary ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
var exporter = GetCustomSerializer( value );
|
||||
|
||||
if ( exporter != null )
|
||||
{
|
||||
jsonObject.Set( name, exporter.Serialize( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonObject.Set( name, CreateObject( value ) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class JSONSerializationSettings
|
||||
{
|
||||
public static readonly string NUMBER_TYPE = "number-type";
|
||||
public static readonly string NUMBER_VALUE = "number-value";
|
||||
public static readonly string INT_NUMBER_TYPE = "int";
|
||||
public static readonly string FLOAT_NUMBER_TYPE = "float";
|
||||
public static readonly string DOUBLE_NUMBER_TYPE = "double";
|
||||
|
||||
static List<Type> serializableDictionaryKeyTypes = new List<Type>()
|
||||
{
|
||||
typeof( string ), typeof( int )
|
||||
};
|
||||
|
||||
public static readonly List<CustomSerializer> defaultSerializers = new List<CustomSerializer>()
|
||||
{
|
||||
new DateTimeSerializer(),
|
||||
new BigIntegerSerializer()
|
||||
};
|
||||
|
||||
|
||||
public static bool IsSerializableDictionary( Type type )
|
||||
{
|
||||
if ( ! ReflectionHelper.IsDictionary( type ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var keyType = type.GetGenericArguments()[ 0 ];
|
||||
|
||||
return serializableDictionaryKeyTypes.IndexOf( keyType ) != -1;
|
||||
}
|
||||
|
||||
|
||||
public static bool IsSerializableDictionary( object value )
|
||||
{
|
||||
if ( ! ReflectionHelper.IsDictionary( value ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var keyType = value.GetType().GetGenericArguments()[ 0 ];
|
||||
|
||||
return serializableDictionaryKeyTypes.IndexOf( keyType ) != -1;
|
||||
}
|
||||
|
||||
|
||||
public List<CustomSerializer> customSerializers = new List<CustomSerializer>();
|
||||
public bool saveNumbersWithType = false;
|
||||
public bool throwErrorOnCycles = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System;
|
||||
using Godot;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public enum ReferenceType
|
||||
{
|
||||
MEMBER_REFERENCE,
|
||||
ARRAY_LIST_REFERENCE,
|
||||
MAP_DICTIONARY_REFERENCE
|
||||
}
|
||||
|
||||
public class Reference
|
||||
{
|
||||
public ReferenceType type;
|
||||
public object target;
|
||||
public int index;
|
||||
public string name;
|
||||
|
||||
public string GetInfo()
|
||||
{
|
||||
var accessorInfo = "";
|
||||
|
||||
if ( ReferenceType.ARRAY_LIST_REFERENCE == type )
|
||||
{
|
||||
if ( target is JSONArray )
|
||||
{
|
||||
accessorInfo += "JSONArray[ " + index + " ]";
|
||||
}
|
||||
else if ( target is IList )
|
||||
{
|
||||
accessorInfo += "List[ " + index + " ]";
|
||||
}
|
||||
else
|
||||
{
|
||||
accessorInfo += "List/Array[ " + index + " ]";
|
||||
}
|
||||
|
||||
}
|
||||
else if ( ReferenceType.MEMBER_REFERENCE == type )
|
||||
{
|
||||
accessorInfo += "Member[ " + name + " ]";
|
||||
}
|
||||
else if ( ReferenceType.MAP_DICTIONARY_REFERENCE == type )
|
||||
{
|
||||
if ( JSONSerializationSettings.IsSerializableDictionary( target ) )
|
||||
{
|
||||
var keyType = target.GetType().GetGenericArguments()[ 0 ];
|
||||
|
||||
if ( typeof( int ) == keyType )
|
||||
{
|
||||
accessorInfo += "Dictionary[ " + index + " ]";
|
||||
}
|
||||
else if ( typeof( string ) == keyType )
|
||||
{
|
||||
accessorInfo += "Dictionary[ " + name + " ]";
|
||||
}
|
||||
}
|
||||
else if ( target is JSONObject )
|
||||
{
|
||||
accessorInfo += "JSONObject[ " + name + " ]";
|
||||
}
|
||||
else
|
||||
{
|
||||
accessorInfo += "Map/Dictionary[ " + index + " ]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return accessorInfo + "@" + target;
|
||||
}
|
||||
|
||||
public void AssignValue( object value )
|
||||
{
|
||||
if ( ReferenceType.ARRAY_LIST_REFERENCE == type )
|
||||
{
|
||||
if ( target is IList )
|
||||
{
|
||||
var list = ( (IList) target );
|
||||
var missing = Mathf.Max( 0, index - ( list.Count - 1 ) );
|
||||
|
||||
for ( int i = 0; i < missing; i++ )
|
||||
{
|
||||
list.Add( null );
|
||||
}
|
||||
|
||||
list[ index ] = value;
|
||||
}
|
||||
else if ( target is JSONArray )
|
||||
{
|
||||
( (JSONArray) target ).Set( index, (JSONData) value );
|
||||
}
|
||||
}
|
||||
else if ( ReferenceType.MEMBER_REFERENCE == type )
|
||||
{
|
||||
if ( target is JSONObject )
|
||||
{
|
||||
( (JSONObject) target ).Set( name, (JSONData) value );
|
||||
}
|
||||
else
|
||||
{
|
||||
ReflectionHelper.SetMemberValue( target, name, value );
|
||||
}
|
||||
|
||||
}
|
||||
else if ( ReferenceType.MAP_DICTIONARY_REFERENCE == type )
|
||||
{
|
||||
if ( target is JSONObject )
|
||||
{
|
||||
( (JSONObject) target ).Set( name, (JSONData) value );
|
||||
}
|
||||
else if ( JSONSerializationSettings.IsSerializableDictionary( target ) )
|
||||
{
|
||||
if ( typeof( string ) == target.GetType().GetGenericArguments()[ 0 ] )
|
||||
{
|
||||
( (IDictionary) target )[ name ] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
( (IDictionary) target )[ index ] = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Reference( object target, int index )
|
||||
{
|
||||
this.target = target;
|
||||
this.index = index;
|
||||
|
||||
if ( target is JSONObject || JSONSerializationSettings.IsSerializableDictionary( target ) )
|
||||
{
|
||||
type = ReferenceType.MAP_DICTIONARY_REFERENCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = ReferenceType.ARRAY_LIST_REFERENCE;
|
||||
}
|
||||
}
|
||||
|
||||
public Reference( object target, string name )
|
||||
{
|
||||
this.target = target;
|
||||
this.name = name;
|
||||
|
||||
if ( target is JSONObject || JSONSerializationSettings.IsSerializableDictionary( target ) )
|
||||
{
|
||||
type = ReferenceType.MAP_DICTIONARY_REFERENCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = ReferenceType.MEMBER_REFERENCE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class Lexer
|
||||
{
|
||||
Dictionary<string,List<LexerMatcher>> _modes = new Dictionary<string, List<LexerMatcher>>();
|
||||
bool _hasError =false;
|
||||
|
||||
public bool hasError => _hasError;
|
||||
|
||||
void AddMatcher( LexerMatcher matcher )
|
||||
{
|
||||
var list = _modes.ContainsKey( matcher.mode ) ? _modes[ matcher.mode ] : null;
|
||||
|
||||
if ( list == null )
|
||||
{
|
||||
list = new List<LexerMatcher>();
|
||||
_modes[ matcher.mode ] = list;
|
||||
}
|
||||
|
||||
list.Add( matcher );
|
||||
}
|
||||
|
||||
public void AddAllMatchers( params LexerMatcher[] matchers )
|
||||
{
|
||||
for ( int i = 0; i < matchers.Length; i++ )
|
||||
{
|
||||
AddMatcher( matchers[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
public void AddMatcher( string type, Regex regex, string mode = "", string nextMode = null )
|
||||
{
|
||||
AddMatcher( new LexerMatcher( type, regex, mode, nextMode ) );
|
||||
}
|
||||
|
||||
public void AddMatcher( string type, string regex, string mode = "", string nextMode = null )
|
||||
{
|
||||
AddMatcher( type, new Regex( regex ), mode, nextMode );
|
||||
}
|
||||
|
||||
public void Lex( string source, System.Action<LexerEvent> callback, int offset = 0, string mode = "" )
|
||||
{
|
||||
var ERROR_FLAG = -1;
|
||||
var DONE_FLAG = -2;
|
||||
|
||||
var lexerEvent = new LexerEvent( "", 0, -2 );
|
||||
|
||||
while ( offset < source.Length )
|
||||
{
|
||||
if ( ! _modes.ContainsKey( mode ) )
|
||||
{
|
||||
var errorMessage = "@Lexer-Error. Mode not found: '" + mode + "'";
|
||||
RJLog.Log( errorMessage, "@", offset );
|
||||
lexerEvent.set( errorMessage, offset, ERROR_FLAG );
|
||||
_hasError = true;
|
||||
callback( lexerEvent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var matchers = _modes[ mode ];
|
||||
var foundSomething = false;
|
||||
|
||||
for ( var i = 0; i < matchers.Count; i++ )
|
||||
{
|
||||
var matcher = matchers[ i ];
|
||||
var matchLength = matcher.MatchLength( source, offset );
|
||||
|
||||
if ( matchLength > 0 )
|
||||
{
|
||||
lexerEvent.set( matcher.type, offset, matchLength );
|
||||
//Logs.Log(matcher.type, ">>", "'"+source.Substring( offset, matchLength )+"'", "@", offset, matchLength );
|
||||
callback( lexerEvent );
|
||||
|
||||
foundSomething = true;
|
||||
|
||||
i = matchers.Count;
|
||||
|
||||
if ( matcher.nextMode != null )
|
||||
{
|
||||
mode = matcher.nextMode;
|
||||
}
|
||||
|
||||
offset += matchLength;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! foundSomething )
|
||||
{
|
||||
var errorMessage = "@Lexer-Error. No match: '" + mode + "'";
|
||||
RJLog.Log(errorMessage, "@", offset );
|
||||
lexerEvent.set( errorMessage, offset, ERROR_FLAG );
|
||||
_hasError = true;
|
||||
callback( lexerEvent );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lexerEvent.set( mode, offset, DONE_FLAG );
|
||||
callback( lexerEvent );
|
||||
}
|
||||
|
||||
public List<LexerEvent> LexToList( string source, int offset = 0, string mode = "" )
|
||||
{
|
||||
var list = new List<LexerEvent>();
|
||||
|
||||
Lex(
|
||||
source,
|
||||
( LexerEvent token )=>
|
||||
{
|
||||
list.Add( token.Copy() );
|
||||
},
|
||||
offset, mode
|
||||
);
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class LexerEvent
|
||||
{
|
||||
string _type;
|
||||
int _offset;
|
||||
int _length;
|
||||
string _match;
|
||||
|
||||
public string match => _match;
|
||||
|
||||
public LexerEvent( string type, int offset, int length )
|
||||
{
|
||||
set( type, offset, length );
|
||||
}
|
||||
|
||||
public string type { get { return _type; } }
|
||||
public int offset { get { return _offset; } }
|
||||
public int length { get { return _length; } }
|
||||
|
||||
public bool isError
|
||||
{
|
||||
get { return this.length == -1; }
|
||||
}
|
||||
|
||||
public bool isDone
|
||||
{
|
||||
get { return this.length == -2; }
|
||||
}
|
||||
|
||||
public void set( string type, int offset, int length )
|
||||
{
|
||||
this._type = type;
|
||||
this._offset = offset;
|
||||
this._length = length;
|
||||
}
|
||||
|
||||
public LexerEvent Copy()
|
||||
{
|
||||
return new LexerEvent( _type, _offset, _length );
|
||||
}
|
||||
|
||||
public int end
|
||||
{
|
||||
get { return this._offset + _length; }
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return "Token{ '" + type + "' (" + offset + "-" + end + ") }";
|
||||
}
|
||||
|
||||
public void GrabMatch( string source )
|
||||
{
|
||||
if ( _length < 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_match = source.Substring( offset, length );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class CSharpLexer:Lexer
|
||||
{
|
||||
public static List<LexerEvent> Lex( string source )
|
||||
{
|
||||
var lexer = new CSharpLexer();
|
||||
var events = lexer.LexToList( source );
|
||||
|
||||
if ( lexer.hasError )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
events.ForEach( ev => { ev.GrabMatch( source ); } );
|
||||
return events;
|
||||
}
|
||||
|
||||
public CSharpLexer()
|
||||
{
|
||||
AddAllMatchers(
|
||||
LexerMatcherLibrary.SINGLE_LINE_COMMENT_MATCHER,
|
||||
LexerMatcherLibrary.MULTI_LINE_COMMENT_MATCHER,
|
||||
LexerMatcherLibrary.DOUBLE_QUOTED_STRING_MATCHER,
|
||||
LexerMatcherLibrary.SINGLE_QUOTED_STRING_MATCHER,
|
||||
LexerMatcherLibrary.C_INSTRUCTION_MATCHER,
|
||||
LexerMatcherLibrary.NUMBER_MATCHER,
|
||||
LexerMatcherLibrary.NULL_MATCHER,
|
||||
LexerMatcherLibrary.BOOL_MATCHER,
|
||||
LexerMatcherLibrary.BREAK_MATCHER,
|
||||
LexerMatcherLibrary.WHITESPACE_MATCHER,
|
||||
LexerMatcherLibrary.LOGIC_MATCHER,
|
||||
LexerMatcherLibrary.BRACKET_MATCHER,
|
||||
LexerMatcherLibrary.ACCESS_MODIFIER_MATCHER,
|
||||
LexerMatcherLibrary.CLASS_MATCHER,
|
||||
LexerMatcherLibrary.OPERATOR_MATCHER,
|
||||
LexerMatcherLibrary.CFUNCTION_MATCHER,
|
||||
LexerMatcherLibrary.CWORD_MATCHER,
|
||||
LexerMatcherLibrary.ANY_SYMBOL_MATCHER
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class LexerMatcher
|
||||
{
|
||||
string _mode;
|
||||
public string mode => _mode;
|
||||
|
||||
string _type;
|
||||
public string type => _type;
|
||||
|
||||
string _nextMode;
|
||||
public string nextMode => _nextMode;
|
||||
|
||||
Regex _matcher;
|
||||
Regex matcher
|
||||
{
|
||||
get { return this._matcher; }
|
||||
}
|
||||
|
||||
public bool Matches( LexerEvent le )
|
||||
{
|
||||
return type == le.type;
|
||||
}
|
||||
|
||||
public LexerMatcher( string type, Regex matcher, string mode = "", string nextMode = "" )
|
||||
{
|
||||
_type = type;
|
||||
_mode = mode;
|
||||
_nextMode = nextMode;
|
||||
_matcher = RegexUtility.MakeSticky( matcher );
|
||||
}
|
||||
|
||||
public LexerMatcher( string type, string matcher, string mode = "", string nextMode = "" )
|
||||
{
|
||||
_type = type;
|
||||
_mode = mode;
|
||||
_nextMode = nextMode;
|
||||
_matcher = RegexUtility.MakeSticky( new Regex( matcher ) );
|
||||
}
|
||||
|
||||
public LexerMatcher CloneWithMode( string mode, string nextMode )
|
||||
{
|
||||
return new LexerMatcher( _type, _matcher, mode, nextMode );
|
||||
}
|
||||
|
||||
public int MatchLength( string source, int offset )
|
||||
{
|
||||
var match = matcher.Match( source, offset );
|
||||
|
||||
if ( match.Success && match.Index != offset )
|
||||
{
|
||||
var message = "Illegal match index! Not a sticky matcher: " + matcher + ".";
|
||||
message += "Offset: " + offset + " Matched Index: " + match.Index ;
|
||||
throw new System.Exception( message );
|
||||
}
|
||||
|
||||
if ( match.Success )
|
||||
{
|
||||
return match.Groups[ 0 ].Length;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class LexerMatcherLibrary
|
||||
{
|
||||
public static readonly LexerMatcher CWORD_MATCHER =
|
||||
new LexerMatcher( "CWORD", @"[a-zA-Z_]\w*" );
|
||||
|
||||
public static readonly LexerMatcher CFUNCTION_MATCHER =
|
||||
new LexerMatcher( "CFUNCTION", @"[a-zA-Z_]\w*(?=\s*\()" );
|
||||
|
||||
// public static readonly LexerMatcher LexerMatcherCLASSNAME_MATCHER =
|
||||
// new LexerMatcher( "CLASSNAME", @"(?<=(?:(?:class|interface|struct)\s+))[a-zA-Z_]\w*" );
|
||||
|
||||
public static readonly LexerMatcher JSWORD_MATCHER =
|
||||
new LexerMatcher( "JSWORD", @"[a-zA-Z_\$]\w*" );
|
||||
|
||||
public static readonly LexerMatcher PHPWORD_MATCHER =
|
||||
new LexerMatcher( "PHPWORD", @"\$?[a-zA-Z_]\w*" );
|
||||
|
||||
|
||||
public static readonly LexerMatcher CSS_CLASS_SELECTOR_MATCHER =
|
||||
new LexerMatcher( "CSS_CLASS_SELECTOR", @"\.[a-zA-Z_](\w|\-)*" );
|
||||
|
||||
public static readonly LexerMatcher CSS_ID_SELECTOR_MATCHER =
|
||||
new LexerMatcher( "CSS_ID_SELECTOR", @"\#[a-zA-Z_](\w|\-)*" );
|
||||
|
||||
public static readonly LexerMatcher CSS_WORD_MATCHER =
|
||||
new LexerMatcher( "CSS_WORD", @"[a-zA-Z_](\w|\-)*" );
|
||||
|
||||
|
||||
public static readonly LexerMatcher HTML_CUSTOM_ELEMENT_MATCHER =
|
||||
new LexerMatcher( "HTML_CUSTOM_ELEMENT", @"[a-zA-Z]\w*\-(\w|\-)+" );
|
||||
|
||||
public static readonly LexerMatcher DOUBLE_QUOTED_STRING_MATCHER =
|
||||
new LexerMatcher( "DOUBLE_QUOTED_STRING", "\"(?:[^\"\\\\]|\\\\.)*\"" );
|
||||
|
||||
public static readonly LexerMatcher SINGLE_QUOTED_STRING_MATCHER =
|
||||
new LexerMatcher( "SINGLE_QUOTED_STRING", @"'(?:[^'\\]|\\.)*'" );
|
||||
|
||||
public static readonly LexerMatcher NUMBER_MATCHER =
|
||||
new LexerMatcher( "NUMBER", @"(?=\.\d|\d)(?:\d+)?(?:\.?\d*)(?:[eE][+-]?\d+)?" );
|
||||
|
||||
public static readonly LexerMatcher WHITESPACE_MATCHER =
|
||||
new LexerMatcher( "WHITESPACE", @"\s+" );
|
||||
|
||||
public static readonly LexerMatcher BREAK_MATCHER =
|
||||
new LexerMatcher( "BREAK", @"(\r\n|\r|\n)" );
|
||||
|
||||
|
||||
public static readonly LexerMatcher NULL_MATCHER =
|
||||
new LexerMatcher( "NULL", "null" );
|
||||
|
||||
public static readonly LexerMatcher BOOL_MATCHER =
|
||||
new LexerMatcher( "BOOL", "true|false" );
|
||||
|
||||
public static readonly LexerMatcher LOGIC_MATCHER =
|
||||
new LexerMatcher( "LOGIC", "if|else|switch|do|while|for|break|continue|return" );
|
||||
|
||||
public static readonly LexerMatcher OPERATOR_MATCHER =
|
||||
new LexerMatcher( "OPERATOR", "(?:\\=\\=)|(?:\\+\\+)|(?:\\-\\-)|\\+|\\-|\\*|\\/|\\^|\\||\\~|\\&|\\%|\\<|\\>|\\=|\\!|\\.|\\:|\\,|\\;" );
|
||||
|
||||
public static readonly LexerMatcher BRACKET_MATCHER =
|
||||
new LexerMatcher( "BRACKET", @"\(|\)|\[|\]|\{|\}" );
|
||||
|
||||
public static readonly LexerMatcher BLOCKSTART_MATCHER =
|
||||
new LexerMatcher( "BLOCKSTART", @"\{" );
|
||||
|
||||
public static readonly LexerMatcher BLOCKEND_MATCHER =
|
||||
new LexerMatcher( "BLOCKEND", @"\}" );
|
||||
|
||||
public static readonly LexerMatcher CLASS_MATCHER =
|
||||
new LexerMatcher( "CLASS", @"\bclass\b" );
|
||||
|
||||
public static readonly LexerMatcher ACCESS_MODIFIER_MATCHER =
|
||||
new LexerMatcher( "ACCESS_MODIFIER", @"\b(?:public|protected|private)\b" );
|
||||
|
||||
|
||||
public static readonly LexerMatcher SINGLE_LINE_COMMENT_MATCHER =
|
||||
new LexerMatcher( "SINGLE_LINE_COMMENT", @"//.*" );
|
||||
|
||||
public static readonly LexerMatcher C_INSTRUCTION_MATCHER =
|
||||
new LexerMatcher( "C_INSTRUCTION", @"\#.*" );
|
||||
|
||||
public static readonly LexerMatcher MULTI_LINE_COMMENT_MATCHER =
|
||||
new LexerMatcher( "MULTI_LINE_COMMENT", @"\/\*(.|(\r\n|\r|\n))*?\*\/" );
|
||||
|
||||
public static readonly LexerMatcher ANY_SYMBOL_MATCHER =
|
||||
new LexerMatcher( "ANY_SYMBOL", @"." );
|
||||
|
||||
public static readonly LexerMatcher HASH_TAG =
|
||||
new LexerMatcher( "HASH_TAG", @"\#(\w|-|\d)+" );
|
||||
|
||||
public static readonly LexerMatcher URL =
|
||||
new LexerMatcher( "URL", @"https?\:\/\/(\w|\.|\-|\?|\=|\+|\/)+" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
|
||||
using System.Globalization;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RegexBuilder
|
||||
{
|
||||
List<string> _parts = new List<string>();
|
||||
List<string> _flags = new List<string>();
|
||||
|
||||
public static RegexBuilder Create()
|
||||
{
|
||||
var rb = new RegexBuilder();
|
||||
return rb;
|
||||
}
|
||||
|
||||
public RegexBuilder this[ string key ]
|
||||
{
|
||||
get { _parts.Add( RegexUtility.EscapeForRegex( key ) ); return this; }
|
||||
}
|
||||
|
||||
public RegexBuilder _
|
||||
{
|
||||
get
|
||||
{
|
||||
_parts.Add( @"\s*" );
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public RegexBuilder __
|
||||
{
|
||||
get
|
||||
{
|
||||
_parts.Add( @"\s+" );
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public RegexBuilder Word
|
||||
{
|
||||
get
|
||||
{
|
||||
_parts.Add( @"(\w+)" );
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public RegexBuilder groupStart
|
||||
{
|
||||
get
|
||||
{
|
||||
_parts.Add( "(" );
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public RegexBuilder groupEnd
|
||||
{
|
||||
get
|
||||
{
|
||||
_parts.Add( ")" );
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public RegexBuilder word
|
||||
{
|
||||
get
|
||||
{
|
||||
_parts.Add( @"\w+" );
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public RegexBuilder flags( string flags )
|
||||
{
|
||||
for ( int i = 0 ; i < flags.Length; i++ )
|
||||
{
|
||||
_flags.Add( flags[ i ] + "" );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Regex ToRegex()
|
||||
{
|
||||
var options = RegexOptions.None;
|
||||
var pattern = Lists.Join( _parts, "" );
|
||||
|
||||
if ( _flags.IndexOf( "i" ) != -1 )
|
||||
{
|
||||
options = options | RegexOptions.IgnoreCase;
|
||||
}
|
||||
|
||||
if ( _flags.IndexOf( "m" ) != -1 )
|
||||
{
|
||||
options = options | RegexOptions.Multiline;
|
||||
}
|
||||
|
||||
if ( _flags.IndexOf( "y" ) != -1 )
|
||||
{
|
||||
pattern = RegexUtility.MakeSourceSticky( pattern );
|
||||
}
|
||||
|
||||
return new Regex( pattern, options );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,520 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
|
||||
using System.Globalization;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class RegexUtility
|
||||
{
|
||||
public static Regex MakeSticky( Regex regex )
|
||||
{
|
||||
var source = MakeSourceSticky( regex + "" );
|
||||
|
||||
return new Regex( source, regex.Options );
|
||||
}
|
||||
|
||||
public static string MakeSourceSticky( string source )
|
||||
{
|
||||
if ( ! source.StartsWith( "\\G" ) )
|
||||
{
|
||||
source = "\\G(?:" + source + ")";
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
public static string WriteDouble( double value )
|
||||
{
|
||||
try
|
||||
{
|
||||
var specifier = "G";
|
||||
|
||||
return value.ToString( specifier, CultureInfo.InvariantCulture );
|
||||
}
|
||||
catch( System.Exception e )
|
||||
{
|
||||
RJLog.Log(e );
|
||||
}
|
||||
|
||||
return "0";
|
||||
}
|
||||
|
||||
public static double ParseDouble( string source, double alternative = 0 )
|
||||
{
|
||||
try
|
||||
{
|
||||
double newValue = System.Double.Parse( source, CultureInfo.InvariantCulture );
|
||||
return newValue;
|
||||
}
|
||||
catch ( System.Exception e )
|
||||
{
|
||||
if ( e != null )
|
||||
{
|
||||
|
||||
}
|
||||
return alternative;
|
||||
}
|
||||
}
|
||||
|
||||
public static float ParseFloat( string source, float alternative = 0 )
|
||||
{
|
||||
return ( float ) ParseDouble( source, alternative );
|
||||
}
|
||||
|
||||
/*public static int ParseInt( string source, int alternative = 0 )
|
||||
{
|
||||
return Mathf.RoundToInt( ( float ) ParseDouble( source, alternative ) );
|
||||
}*/
|
||||
|
||||
public static int ParseInt( string source, int alternative = 0 )
|
||||
{
|
||||
var multiply = 1;
|
||||
var value = 0;
|
||||
|
||||
for ( int i = source.Length - 1; i >= 0 ; i-- )
|
||||
{
|
||||
var symbol = source[ i ];
|
||||
|
||||
if ( i == 0 )
|
||||
{
|
||||
if ( symbol == '-' )
|
||||
{ return -value; }
|
||||
|
||||
if ( symbol == '+' )
|
||||
{ return value; }
|
||||
}
|
||||
|
||||
var digitValue = ParseDigit( symbol );
|
||||
|
||||
if ( digitValue == -1 )
|
||||
{
|
||||
return alternative;
|
||||
}
|
||||
|
||||
value += digitValue * multiply;
|
||||
|
||||
multiply *= 10;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int ParseDigit( char c )
|
||||
{
|
||||
switch ( c )
|
||||
{
|
||||
case '0': return 0;
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 3;
|
||||
case '4': return 4;
|
||||
case '5': return 5;
|
||||
case '6': return 6;
|
||||
case '7': return 7;
|
||||
case '8': return 8;
|
||||
case '9': return 9;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static bool Matching( string source, string regex )
|
||||
{
|
||||
return new Regex( regex ).Match( source ).Success;
|
||||
}
|
||||
|
||||
public static string Substring( string source, string regex )
|
||||
{
|
||||
return new Regex( regex ).Match( source ).Value;
|
||||
}
|
||||
|
||||
public static string EscapeForRegex( string source )
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
var escapes = "+*?^$\\.[]{}()|/";
|
||||
|
||||
for ( int i = 0; i < source.Length; i++ )
|
||||
{
|
||||
var character = source[ i ];
|
||||
|
||||
if ( escapes.IndexOf( character ) != -1 )
|
||||
{
|
||||
sb.Append( "\\" );
|
||||
}
|
||||
|
||||
sb.Append( character );
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string LeadingZeros( int value, int minDigits = 2)
|
||||
{
|
||||
var stringValue = value + "";
|
||||
|
||||
while ( stringValue.Length < minDigits )
|
||||
{
|
||||
stringValue = "0" + stringValue;
|
||||
}
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
public static string NumberWithThousandsSeperator( int value, string seperator = "." )
|
||||
{
|
||||
var isNegative = value < 0;
|
||||
|
||||
if ( isNegative ){ value = - value; }
|
||||
|
||||
var stringValue = value + "";
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
for ( int i = 0; i < stringValue.Length; i++ )
|
||||
{
|
||||
var index = ( stringValue.Length - 1 ) - i ;
|
||||
|
||||
if ( i % 4 == 3 )
|
||||
{
|
||||
builder.Append( seperator );
|
||||
}
|
||||
|
||||
builder.Append( stringValue[ index ] );
|
||||
}
|
||||
|
||||
var seperatorValue = builder.ToString();
|
||||
|
||||
return Reverse( seperatorValue );
|
||||
}
|
||||
|
||||
public static string Reverse( string source )
|
||||
{
|
||||
char[] characters = source.ToCharArray();
|
||||
System.Array.Reverse( characters );
|
||||
return new string( characters );
|
||||
}
|
||||
|
||||
public static Regex EscapedOrRegex( string first, params string[] other )
|
||||
{
|
||||
var value = EscapeForRegex( first );
|
||||
|
||||
for ( var i = 0; i < other.Length; i++ )
|
||||
{
|
||||
value += "|" + EscapeForRegex( other[ i ] );
|
||||
|
||||
/*
|
||||
|
||||
if ( i == 0 )
|
||||
{
|
||||
value = "(" + value;
|
||||
}
|
||||
value += ")|(" + EscapeForRegex( other[ i ] );
|
||||
|
||||
if ( i == ( other.Length - 1 ) )
|
||||
{
|
||||
value += ")";
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
//Logs.Log("EscapedOrRegex:", "'" + value + "'");
|
||||
return new Regex( value );
|
||||
|
||||
}
|
||||
|
||||
public static Color ParseColor( string source, Color alternative )
|
||||
{
|
||||
if ( Matching( source, @"#\d+" ) )
|
||||
{
|
||||
return ParseHexColor( source, alternative );
|
||||
}
|
||||
else if ( Matching( source, @"^rgb" ) )
|
||||
{
|
||||
return ParseRGBColor( source, alternative );
|
||||
}
|
||||
else if ( Matching( source, @"^hsl" ) )
|
||||
{
|
||||
return ParseHSLColor( source, alternative );
|
||||
}
|
||||
|
||||
return alternative;
|
||||
}
|
||||
|
||||
|
||||
public static double ParsePercentage( string source )
|
||||
{
|
||||
source = Remove( source, @"\s*%" );
|
||||
return ParseDouble( source.Trim(), 0 ) / 100.0;
|
||||
}
|
||||
|
||||
public static int ParseHexCharacter( char hexCharacter )
|
||||
{
|
||||
if ( '0' <= hexCharacter && hexCharacter <= '9' )
|
||||
{
|
||||
return (int) (hexCharacter - '0' );
|
||||
}
|
||||
|
||||
if ( 'a' <= hexCharacter && hexCharacter <= 'f' )
|
||||
{
|
||||
return (int) (hexCharacter - 'a') + 10;
|
||||
}
|
||||
|
||||
if ( 'A' <= hexCharacter && hexCharacter <= 'F' )
|
||||
{
|
||||
return (int) (hexCharacter - 'A') + 10;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int ParseHex( string source )
|
||||
{
|
||||
var value = 0;
|
||||
var shift = 0;
|
||||
|
||||
for ( var i = source.Length - 1; i < source.Length; i++ )
|
||||
{
|
||||
var hexValue = ParseHexCharacter( source[ i ] ) << shift;
|
||||
shift += 4;
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
public static Color ParseHexColor( string source, Color alternative )
|
||||
{
|
||||
source = RegexUtility.Remove( source, @"^#" );
|
||||
|
||||
var numbers = new List<float>();
|
||||
|
||||
for ( var i = 0; i < 3; i++ )
|
||||
{
|
||||
var value = source.Substring( i * 2, 2 );
|
||||
var numberValue = ParseHex( value ) / 255f;
|
||||
numbers.Add( numberValue );
|
||||
}
|
||||
|
||||
if ( numbers.Count == 3 )
|
||||
{
|
||||
numbers.Add( 1f );
|
||||
}
|
||||
|
||||
return new Color( numbers[ 0 ], numbers[ 1 ], numbers[ 2 ] , numbers[ 3 ] );
|
||||
|
||||
}
|
||||
|
||||
public static Color ParseRGBColor( string source, Color alternative )
|
||||
{
|
||||
source = RegexUtility.Remove( source, @"^rgba?\(" );
|
||||
source = RegexUtility.Remove( source, @"\)$" );
|
||||
|
||||
var splits = Split( source, @"\," );
|
||||
var numbers = Lists.Map<string,float>( splits,
|
||||
( string value, int index ) =>
|
||||
{
|
||||
if ( value.Contains( "%" ) )
|
||||
{
|
||||
return (float) ParsePercentage( value );
|
||||
}
|
||||
|
||||
return ParseFloat( value );
|
||||
}
|
||||
);
|
||||
|
||||
if ( numbers.Count == 3 )
|
||||
{
|
||||
numbers.Add( 1f );
|
||||
}
|
||||
|
||||
return new Color( numbers[ 0 ], numbers[ 1 ], numbers[ 2 ] , numbers[ 3 ] );
|
||||
|
||||
}
|
||||
|
||||
public static HSLColor ParseHSLColor( string source, HSLColor alternative )
|
||||
{
|
||||
source = RegexUtility.Remove( source, @"^hsl\(" );
|
||||
source = RegexUtility.Remove( source, @"\)$" );
|
||||
|
||||
var splits = Split( source, @"\," );
|
||||
var numbers =Lists.Map<string,float>( splits,
|
||||
( string value, int index ) =>
|
||||
{
|
||||
if ( value.Contains( "%" ) )
|
||||
{
|
||||
return (float) ParsePercentage( value );
|
||||
}
|
||||
|
||||
return ParseFloat( value );
|
||||
}
|
||||
);
|
||||
|
||||
if ( numbers.Count < 3 )
|
||||
{
|
||||
RJLog.Log("Not enough numbers parsed: ", source, ">>", numbers.Count, Lists.Join( numbers, "," ) );
|
||||
}
|
||||
|
||||
|
||||
if ( numbers.Count == 3 )
|
||||
{
|
||||
numbers.Add( 1f );
|
||||
}
|
||||
|
||||
return new HSLColor( numbers[ 0 ], numbers[ 1 ], numbers[ 2 ] , numbers[ 3 ] );
|
||||
}
|
||||
|
||||
public static List<string> Split( string source, Regex regex )
|
||||
{
|
||||
var strings = regex.Split( source );
|
||||
var list = new List<string>();
|
||||
|
||||
list.AddRange( strings );
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<string> Split( string source, string regex )
|
||||
{
|
||||
return Split( source, new Regex( regex ) );
|
||||
}
|
||||
|
||||
public static string Remove( string source, string regex )
|
||||
{
|
||||
return Replace( source, regex, "" );
|
||||
}
|
||||
|
||||
public static string Replace( string source, Regex regex, string replacement )
|
||||
{
|
||||
return regex.Replace( source, replacement );
|
||||
}
|
||||
|
||||
public static string Remove( string source, Regex regex )
|
||||
{
|
||||
return Replace( source, regex, "" );
|
||||
}
|
||||
|
||||
public static string Replace( string source, string regex, string replacement )
|
||||
{
|
||||
return new Regex( regex ).Replace( source, replacement );
|
||||
}
|
||||
|
||||
public static string ReplaceMultiple( string source, Dictionary<string,string> replacements )
|
||||
{
|
||||
var replaced = source;
|
||||
|
||||
foreach ( var vk in replacements )
|
||||
{
|
||||
replaced = replaced.Replace( vk.Key, vk.Value );
|
||||
}
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
public static string ParentPathOrLastFragment( string path )
|
||||
{
|
||||
var splits = SplitPaths( path );
|
||||
|
||||
return splits[ splits.Count - 1 ];
|
||||
}
|
||||
|
||||
public static List<string> SplitPaths( string path )
|
||||
{
|
||||
var splittedPaths = new List<string>();
|
||||
var normalizedPath = NormalizePath( path );
|
||||
var splits = Split( normalizedPath, new Regex( @"\/" ) );
|
||||
return splits;
|
||||
}
|
||||
|
||||
public static string JoinPaths( List<string> paths, int startIndex = 0, int length = -1 )
|
||||
{
|
||||
var normalizedPaths = new List<string>();
|
||||
// normalizedPaths.AddRange( paths );
|
||||
|
||||
var endIndex = startIndex + length;
|
||||
var end = ( length < 0 || ( length + startIndex ) >= paths.Count ) ? paths.Count : endIndex;
|
||||
|
||||
for ( var i = startIndex; i < end; i++ )
|
||||
{
|
||||
normalizedPaths.Add( _NormalizePath( paths[ i ] ) );
|
||||
}
|
||||
|
||||
return Lists.Join( normalizedPaths, "/" );
|
||||
}
|
||||
|
||||
public static string Join( string pathA, string pathB, params string[] paths )
|
||||
{
|
||||
var normalizedPaths = new List<string>();
|
||||
normalizedPaths.Add( pathA );
|
||||
normalizedPaths.Add( pathB );
|
||||
normalizedPaths.AddRange( paths );
|
||||
|
||||
for ( var i = 0; i < normalizedPaths.Count; i++ )
|
||||
{
|
||||
normalizedPaths[ i ] = _NormalizePath( normalizedPaths[ i ] );
|
||||
}
|
||||
|
||||
return Lists.Join( normalizedPaths, "/" );
|
||||
}
|
||||
|
||||
private static string _EnsureForwardSlashes( string path )
|
||||
{
|
||||
if ( path.IndexOf( "\\" ) == -1 )
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
var correctedPath = "";
|
||||
|
||||
for ( int i = 0; i < path.Length; i++ )
|
||||
{
|
||||
if ( path[ i ] == '\\' )
|
||||
{
|
||||
correctedPath += "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
correctedPath += path[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
return correctedPath;
|
||||
}
|
||||
|
||||
public static string NormalizePath( string path )
|
||||
{
|
||||
return _NormalizePath( path );
|
||||
}
|
||||
|
||||
public static string WindowsPath( string path )
|
||||
{
|
||||
path = _NormalizePath( path );
|
||||
|
||||
var slashes = @"\/";
|
||||
|
||||
path = Regex.Replace( path, slashes, "\\" );
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private static string _NormalizePath( string path )
|
||||
{
|
||||
path = _EnsureForwardSlashes( path );
|
||||
|
||||
var startSlashes = @"^\/+";
|
||||
var endSlashes = @"\/+$";
|
||||
var multiples = @"\/\/+";
|
||||
|
||||
path = Regex.Replace( path, startSlashes, "" );
|
||||
path = Regex.Replace( path, endSlashes, "" );
|
||||
path = Regex.Replace( path, multiples, "/" );
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class TextAnchor
|
||||
{
|
||||
public readonly int lineIndex;
|
||||
public readonly int characterIndex;
|
||||
|
||||
public TextAnchor( int lineIndex, int characterIndex )
|
||||
{
|
||||
this.lineIndex = lineIndex;
|
||||
this.characterIndex = characterIndex;
|
||||
}
|
||||
|
||||
public TextAnchor Copy()
|
||||
{
|
||||
return new TextAnchor( lineIndex, characterIndex );
|
||||
}
|
||||
|
||||
public string info
|
||||
{
|
||||
get
|
||||
{
|
||||
return lineIndex + " (" + characterIndex + ")";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class TextLine
|
||||
{
|
||||
public readonly int characterIndex;
|
||||
public readonly int length;
|
||||
public readonly int lineIndex;
|
||||
public readonly int lineBreakLength;
|
||||
|
||||
public TextLine( int lineIndex, int characterIndex, int length, int lineBreakLength )
|
||||
{
|
||||
this.characterIndex = characterIndex;
|
||||
this.length = length;
|
||||
this.lineIndex = lineIndex;
|
||||
this.lineBreakLength = lineBreakLength;
|
||||
}
|
||||
|
||||
public int contentLength { get { return length - lineBreakLength; } }
|
||||
public int contentOffset { get { return characterIndex + lineBreakLength; } }
|
||||
public int textEditorLineIndex { get { return lineIndex + 1 ; } }
|
||||
|
||||
public string GetContent( string source )
|
||||
{
|
||||
return source.Substring( contentOffset, contentLength );
|
||||
}
|
||||
|
||||
|
||||
public string info
|
||||
{
|
||||
get
|
||||
{
|
||||
var info = new List<string>();
|
||||
|
||||
info.AddRange( new List<string>{
|
||||
"line index: " + lineIndex,
|
||||
"character index: " + characterIndex,
|
||||
"content length: " + contentLength,
|
||||
"length: " + length,
|
||||
"line break length: " + lineBreakLength
|
||||
}
|
||||
);
|
||||
|
||||
return "{ " + Lists.Join( info, ", " ) + " }";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public TextAnchor GetRawAnchor( int characterIndex )
|
||||
{
|
||||
return new TextAnchor( lineIndex, characterIndex - this.characterIndex );
|
||||
}
|
||||
|
||||
public TextAnchor GetTextEditorAnchor( int characterIndex )
|
||||
{
|
||||
var lineOffset = this.characterIndex + lineBreakLength;
|
||||
var offset = Mathf.Max( 0, characterIndex - lineOffset );
|
||||
return new TextAnchor( lineIndex + 1, offset + 1 );
|
||||
}
|
||||
|
||||
public int lineStart { get { return characterIndex; } }
|
||||
public int lineEnd { get { return ( characterIndex + length ); } }
|
||||
|
||||
public bool Contains( int characterIndex )
|
||||
{
|
||||
return lineStart <= characterIndex && characterIndex <= lineEnd;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class TextLinesMapper
|
||||
{
|
||||
List<TextLine> _lines;
|
||||
int _cachedLineIndex;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
_lines = new List<TextLine>();
|
||||
_cachedLineIndex = 0;
|
||||
}
|
||||
|
||||
public void Map( string source )
|
||||
{
|
||||
Reset();
|
||||
|
||||
var currentCharacterIndex = 0;
|
||||
currentCharacterIndex = NextLineBreak( source, currentCharacterIndex );
|
||||
|
||||
var lastCharacterIndex = 0;
|
||||
var lastLineBreakLength = 0;
|
||||
|
||||
var currentLineIndex = 0;
|
||||
var maxTries = 10000; var currentTries = 0;
|
||||
// currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
|
||||
while ( currentCharacterIndex != -1 )
|
||||
{
|
||||
|
||||
currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
|
||||
var currentBreakLength = LineBreakLength( source, currentCharacterIndex );
|
||||
|
||||
|
||||
var lineLength = currentCharacterIndex - lastCharacterIndex;
|
||||
var lineInfo = new TextLine( currentLineIndex, lastCharacterIndex, lineLength, lastLineBreakLength );
|
||||
_lines.Add( lineInfo );
|
||||
|
||||
lastLineBreakLength = currentBreakLength;
|
||||
lastCharacterIndex = currentCharacterIndex;
|
||||
currentLineIndex ++;
|
||||
|
||||
|
||||
currentCharacterIndex = NextLineBreak( source, currentCharacterIndex + currentBreakLength );
|
||||
}
|
||||
|
||||
var endLineLength = source.Length - lastCharacterIndex;
|
||||
var endLineInfo = new TextLine( currentLineIndex, lastCharacterIndex, endLineLength, lastLineBreakLength );
|
||||
_lines.Add( endLineInfo );
|
||||
|
||||
}
|
||||
|
||||
public int numCharacters { get { return _lines[ _lines.Count -1 ].lineEnd + 1; } }
|
||||
|
||||
public int numLines { get { return _lines.Count; } }
|
||||
public string lineInfos
|
||||
{
|
||||
get
|
||||
{
|
||||
var output = new List<string>();
|
||||
foreach ( var line in _lines )
|
||||
{
|
||||
output.Add( line.info );
|
||||
}
|
||||
|
||||
return Lists.Join( output, ",\n" );
|
||||
}
|
||||
}
|
||||
public TextLine GetLine( int characterIndex )
|
||||
{
|
||||
var lineIndex = GetLineIndex( characterIndex );
|
||||
return lineIndex == -1 ? null : _lines[ lineIndex ];
|
||||
}
|
||||
|
||||
public TextAnchor GetAnchor( int characterIndex, bool forTextEditor )
|
||||
{
|
||||
var line = GetLine( characterIndex );
|
||||
if ( line == null ) { return null; }
|
||||
|
||||
return forTextEditor ? line.GetTextEditorAnchor( characterIndex ) : line.GetRawAnchor( characterIndex );
|
||||
}
|
||||
|
||||
public TextSelection GetSelection( int startCharacterIndex, int endCharacterIndex, bool forTextEditor = false )
|
||||
{
|
||||
var startAnchor = GetAnchor( startCharacterIndex, forTextEditor );
|
||||
var endAnchor = GetAnchor( endCharacterIndex, forTextEditor );
|
||||
|
||||
if ( startAnchor == null || endAnchor == null ) { return null; }
|
||||
|
||||
return new TextSelection( startAnchor, endAnchor );
|
||||
}
|
||||
|
||||
public TextSelection GetTextEditorSelection( int startCharacterIndex, int endCharacterIndex )
|
||||
{
|
||||
return GetSelection( startCharacterIndex, endCharacterIndex, true );
|
||||
}
|
||||
|
||||
public string GetTextEditorInfo( int startCharacterIndex, int endCharacterIndex )
|
||||
{
|
||||
var selection = GetTextEditorSelection( startCharacterIndex, endCharacterIndex );
|
||||
|
||||
if ( selection != null ) { return selection.info; }
|
||||
|
||||
return "@chars(" + startCharacterIndex + "," + endCharacterIndex + ")";
|
||||
}
|
||||
|
||||
public string GetTextEditorSnippet( string source, int startCharacterIndex, int endCharacterIndex )
|
||||
{
|
||||
var startLine = GetLineIndex( startCharacterIndex );
|
||||
var endLine = GetLineIndex( endCharacterIndex );
|
||||
|
||||
if ( endLine < startLine )
|
||||
{
|
||||
var b = endLine; endLine = startLine; startLine = b;
|
||||
}
|
||||
|
||||
var snippet = new StringBuilder();
|
||||
var lengthMaxLineNumberIndex = ( _lines[ endLine ].textEditorLineIndex + "" ).Length;
|
||||
|
||||
for ( var i = startLine; i < endLine; i++ )
|
||||
{
|
||||
var line = _lines[ i ];
|
||||
var lineNumber = _lines[ i ].textEditorLineIndex + "";
|
||||
|
||||
while ( lineNumber.Length < lengthMaxLineNumberIndex )
|
||||
{
|
||||
lineNumber = "0" + lineNumber;
|
||||
}
|
||||
|
||||
var content = line.GetContent( source );
|
||||
snippet.Append( lineNumber + ": " );
|
||||
snippet.Append( content );
|
||||
snippet.Append( "\n" );
|
||||
}
|
||||
|
||||
return snippet.ToString();
|
||||
|
||||
}
|
||||
|
||||
public string GetTextEditorInfo( int characterIndex )
|
||||
{
|
||||
var anchor = GetAnchor( characterIndex, true );
|
||||
|
||||
if ( anchor != null ) { return anchor.info; }
|
||||
|
||||
return "@chars(" + characterIndex + ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
TextLine _cachedLine { get { return _lines[ _cachedLineIndex ]; } }
|
||||
|
||||
public int GetLineIndex( int characterIndex )
|
||||
{
|
||||
if ( _cachedLine.Contains( characterIndex ) )
|
||||
{
|
||||
return _cachedLineIndex;
|
||||
}
|
||||
|
||||
if ( _lines.Count == 0 || characterIndex < 0 || characterIndex > numCharacters )
|
||||
{ return -1; }
|
||||
|
||||
var lineContainsCharacterIndex = false;
|
||||
var lineIndexInRange = true;
|
||||
|
||||
var characterIndexIsHigher = _cachedLine.characterIndex < characterIndex;
|
||||
var searchForward = 1;
|
||||
var searchBackward = -1 ;
|
||||
var searchStep = characterIndexIsHigher ? searchForward : searchBackward;
|
||||
|
||||
var maxTries = 1000; var currentTries = 0;
|
||||
// currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
|
||||
|
||||
do
|
||||
{
|
||||
currentTries ++; if ( currentTries == maxTries ) { throw new System.Exception(); }
|
||||
var nextLineIndex = _cachedLineIndex + searchStep;
|
||||
lineIndexInRange = 0 <= nextLineIndex && nextLineIndex < _lines.Count;
|
||||
|
||||
if ( lineIndexInRange )
|
||||
{
|
||||
_cachedLineIndex = nextLineIndex;
|
||||
lineContainsCharacterIndex = _cachedLine.Contains( characterIndex );
|
||||
}
|
||||
else
|
||||
{
|
||||
lineContainsCharacterIndex = false;
|
||||
}
|
||||
|
||||
}
|
||||
while ( ! lineContainsCharacterIndex && lineIndexInRange );
|
||||
|
||||
if ( lineContainsCharacterIndex )
|
||||
{
|
||||
return _cachedLineIndex;
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
int NextLineBreak( string source, int offset )
|
||||
{
|
||||
for ( int i = offset; i < source.Length; i++ )
|
||||
{
|
||||
if ( source[ i ] == '\n' || source[ i ] == '\r' )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int LineBreakLength( string source, int offset )
|
||||
{
|
||||
if ( source[ offset ] == '\r' && ( ( offset + 1 ) < source.Length ) )
|
||||
{
|
||||
return source[ offset + 1 ] == '\n' ? 2 : 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class TextSelection
|
||||
{
|
||||
TextAnchor _start;
|
||||
TextAnchor _end;
|
||||
|
||||
public TextSelection( TextAnchor start, TextAnchor end )
|
||||
{
|
||||
_start = start.Copy();
|
||||
_end = end.Copy();
|
||||
}
|
||||
|
||||
public TextAnchor start { get { return _start; } }
|
||||
public TextAnchor end { get { return _end; } }
|
||||
public TextSelection Copy() { return new TextSelection( start, end ); }
|
||||
|
||||
public bool isOneLine { get { return _start.lineIndex == _end.lineIndex; } }
|
||||
public bool isAnchor { get { return isOneLine && _start.characterIndex == _end.characterIndex; } }
|
||||
|
||||
public string info
|
||||
{
|
||||
get
|
||||
{
|
||||
if ( isAnchor )
|
||||
{
|
||||
// Output: 12 (1)
|
||||
return start.info;
|
||||
}
|
||||
|
||||
if ( isOneLine )
|
||||
{
|
||||
// Output: 12 (1-6)
|
||||
return start.lineIndex + " (" + start.characterIndex + "-" + end.characterIndex + ")";
|
||||
}
|
||||
|
||||
// Output: 12-15 (3,12)
|
||||
return start.lineIndex + "-" + end.lineIndex +
|
||||
"(" + start.characterIndex + "," + end.characterIndex + ")";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class Lists
|
||||
{
|
||||
public static List<T> ToList<T>( T[] array )
|
||||
{
|
||||
var list = new List<T>();
|
||||
list.AddRange( array );
|
||||
return list;
|
||||
}
|
||||
|
||||
public static bool Has<T>( List<T> list, T item )
|
||||
{
|
||||
return list.IndexOf( item ) != - 1;
|
||||
}
|
||||
|
||||
public static T Pop<T>( List<T> list )
|
||||
{
|
||||
if ( list.Count == 0 ){ return default(T); }
|
||||
|
||||
var element = list[ list.Count - 1 ];
|
||||
list.RemoveAt( list.Count - 1 );
|
||||
return element;
|
||||
}
|
||||
|
||||
public static T Last<T>( List<T> list )
|
||||
{
|
||||
return list.Count == 0 ? default(T) : list[ list.Count - 1 ];
|
||||
}
|
||||
|
||||
|
||||
public static int CountItems<T>( List<T> list, Predicate<T> test )
|
||||
{
|
||||
var result = 0;
|
||||
|
||||
for ( int i = 0; i < list.Count; i++ )
|
||||
{
|
||||
if ( test( list[ i ] ) )
|
||||
{
|
||||
result++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int CountItems<T>( T[] list, Predicate<T> test )
|
||||
{
|
||||
var result = 0;
|
||||
|
||||
for ( int i = 0; i < list.Length; i++ )
|
||||
{
|
||||
if ( test( list[ i ] ) )
|
||||
{
|
||||
result++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool AreEntriesEqual<T>( List<T> a, List<T> b )
|
||||
{
|
||||
if ( a.Count != b.Count )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < a.Count; i++ )
|
||||
{
|
||||
var isEqual = EqualityComparer<T>.Default.Equals( a[ i ], b[ i ]);
|
||||
|
||||
if ( ! isEqual )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static string Join<T>( List<T> array, string seperator = ", " )
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for ( var i = 0; i < array.Count; i++ )
|
||||
{
|
||||
if ( i != 0 )
|
||||
{
|
||||
sb.Append( seperator );
|
||||
}
|
||||
|
||||
sb.Append( array[ i ] );
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static List<T> From<T>( T[] array )
|
||||
{
|
||||
var copy = new List<T>();
|
||||
copy.AddRange( array );
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static List<T> From<T>( List<T> list )
|
||||
{
|
||||
var copy = new List<T>();
|
||||
copy.AddRange( list );
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static void Filter<T>( List<T> inputList, List<T> list, Func<T,int,bool> filter )
|
||||
{
|
||||
for ( int i = 0; i < inputList.Count; i++ )
|
||||
{
|
||||
if ( filter( inputList[ i ], i ) )
|
||||
{
|
||||
list.Add( inputList[ i ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Filter<T>( List<T> inputList, List<T> list, Func<T,bool> filter )
|
||||
{
|
||||
for ( int i = 0; i < inputList.Count; i++ )
|
||||
{
|
||||
if ( filter( inputList[ i ] ) )
|
||||
{
|
||||
list.Add( inputList[ i ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<T> Filter<T>( List<T> inputList, Func<T,bool> filter )
|
||||
{
|
||||
var list = new List<T>();
|
||||
|
||||
for ( int i = 0; i < inputList.Count; i++ )
|
||||
{
|
||||
if ( filter( inputList[ i ] ) )
|
||||
{
|
||||
list.Add( inputList[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<R> FilterType<T,R>( List<T> inputList ) where R:T
|
||||
{
|
||||
var list = new List<R>();
|
||||
|
||||
inputList.ForEach
|
||||
(
|
||||
e =>
|
||||
{
|
||||
if ( e == null || ! typeof( R ).IsAssignableFrom( e.GetType() ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add( (R) e );
|
||||
}
|
||||
);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static T Find<T>( List<T> list, Func<T,bool> callback )
|
||||
{
|
||||
for ( int i = 0; i < list.Count; i++ )
|
||||
{
|
||||
if ( callback( list[ i ] ) )
|
||||
{
|
||||
return list[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
return default( T );
|
||||
}
|
||||
|
||||
public static List<U> Map<T,U>( List<T> inputList, Func<T,int,U> mapper, List<U> list = null )
|
||||
{
|
||||
if ( list == null )
|
||||
{
|
||||
list = new List<U>();
|
||||
}
|
||||
|
||||
for ( int i = 0; i < inputList.Count; i++ )
|
||||
{
|
||||
list.Add( mapper( inputList[ i ], i ) );
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<U> Map<T,U>( T[] inputList, Func<T,int,U> mapper, List<U> list = null )
|
||||
{
|
||||
if ( list == null )
|
||||
{
|
||||
list = new List<U>();
|
||||
}
|
||||
|
||||
for ( int i = 0; i < inputList.Length; i++ )
|
||||
{
|
||||
list.Add( mapper( inputList[ i ], i ) );
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<U> Map<T,U>( List<T> inputList, Func<T,U> mapper, List<U> list = null )
|
||||
{
|
||||
if ( list == null )
|
||||
{
|
||||
list = new List<U>();
|
||||
}
|
||||
|
||||
for ( int i = 0; i < inputList.Count; i++ )
|
||||
{
|
||||
list.Add( mapper( inputList[ i ] ) );
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<U> Map<T,U>( T[] inputList, Func<T,U> mapper, List<U> list = null )
|
||||
{
|
||||
if ( list == null )
|
||||
{
|
||||
list = new List<U>();
|
||||
}
|
||||
|
||||
for ( int i = 0; i < inputList.Length; i++ )
|
||||
{
|
||||
list.Add( mapper( inputList[ i ] ) );
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class ReflectionHelper
|
||||
{
|
||||
public static Type GetTypeByNameFromAssembly( string name, Assembly assembly )
|
||||
{
|
||||
var type = assembly.GetType( name );
|
||||
|
||||
if ( type != null )
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
var types = assembly.GetTypes();
|
||||
|
||||
var genericEndingRegex = @"`\d+$";
|
||||
|
||||
for ( int i = 0; i < types.Length; i++ )
|
||||
{
|
||||
var typeName = types[ i ].Name;
|
||||
var isGeneric = Regex.IsMatch( typeName, genericEndingRegex );
|
||||
|
||||
if ( ! isGeneric )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var typeNameWithoutGeneric = Regex.Replace( typeName, genericEndingRegex, "" );
|
||||
|
||||
if ( typeNameWithoutGeneric == name )
|
||||
{
|
||||
return types[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public static Type GetTypeByName( string name)
|
||||
{
|
||||
var assemblies = new List<Assembly>();
|
||||
assemblies.AddRange( AppDomain.CurrentDomain.GetAssemblies() );
|
||||
assemblies.Reverse();
|
||||
|
||||
var assembly = assemblies.Find( a => a.GetType( name ) != null );
|
||||
|
||||
return assembly == null ? null : assembly.GetType( name );
|
||||
}
|
||||
|
||||
public static bool IsList( Type type )
|
||||
{
|
||||
if ( ! ( type.IsGenericType ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return type.GetGenericTypeDefinition().IsAssignableFrom( typeof( List<> ) );
|
||||
}
|
||||
|
||||
public static bool IsList( object objectValue )
|
||||
{
|
||||
if ( objectValue == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! ( objectValue is IList ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsList( objectValue.GetType() );
|
||||
}
|
||||
|
||||
public static bool IsDictionary( Type type )
|
||||
{
|
||||
if ( ! ( type.IsGenericType ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return type.GetGenericTypeDefinition().IsAssignableFrom( typeof( Dictionary<,> ) );
|
||||
}
|
||||
|
||||
public static bool IsDictionary( object objectValue )
|
||||
{
|
||||
if ( objectValue == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! ( objectValue is IDictionary ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsDictionary( objectValue.GetType() );
|
||||
}
|
||||
|
||||
public static string GetMemberName( object obj, object member )
|
||||
{
|
||||
if ( obj == null || member == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = obj.GetType();
|
||||
var fields = type.GetFields();
|
||||
|
||||
for ( int i = 0; i < fields.Length; i++ )
|
||||
{
|
||||
var value = fields[ i ].GetValue( obj );
|
||||
|
||||
if ( value == member )
|
||||
{
|
||||
return fields[ i ].Name;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public static List<T> GetFieldsOfType<T>( object instance ) where T:class
|
||||
{
|
||||
var type = instance.GetType();
|
||||
var fields = type.GetFields();
|
||||
var list = new List<T>();
|
||||
|
||||
for ( int i = 0; i < fields.Length; i++ )
|
||||
{
|
||||
var value = fields[ i ].GetValue( instance );
|
||||
var tValue = value as T;
|
||||
|
||||
if ( tValue != null )
|
||||
{
|
||||
list.Add( tValue );
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void GetFields<C,T>( System.Action<System.Reflection.FieldInfo> action )
|
||||
{
|
||||
GetFields<T>( typeof( C ), action );
|
||||
}
|
||||
|
||||
public static void GetFields<T>( System.Type c, System.Action<System.Reflection.FieldInfo> action )
|
||||
{
|
||||
var fields = c.GetFields();
|
||||
|
||||
foreach( var f in fields )
|
||||
{
|
||||
if ( f.FieldType == typeof( T ) )
|
||||
{
|
||||
action( f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static System.Reflection.FieldInfo GetFieldInfo( object instance, string memberName )
|
||||
{
|
||||
var type = instance.GetType();
|
||||
var fields = type.GetFields();
|
||||
var index = -1;
|
||||
|
||||
for ( int i = 0; i < fields.Length; i++ )
|
||||
{
|
||||
if ( fields[ i ].Name == memberName )
|
||||
{
|
||||
index = i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return index == -1 ? null : fields[ index ];
|
||||
}
|
||||
|
||||
public static bool HasMember( object instance, string memberName )
|
||||
{
|
||||
return GetFieldInfo( instance, memberName ) != null;
|
||||
}
|
||||
|
||||
public static object GetMemberValue( object instance, string memberName )
|
||||
{
|
||||
try
|
||||
{
|
||||
var field = ReflectionHelper.GetFieldInfo( instance, memberName );
|
||||
|
||||
if ( field == null )
|
||||
{
|
||||
RJLog.Log(instance, "GetValue: member not found with name ", "'" + memberName + "'" );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return field.GetValue( instance );
|
||||
|
||||
}
|
||||
catch( System.Exception e )
|
||||
{
|
||||
RJLog.Log(e );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void SetMemberValue( object instance, string memberName, object value )
|
||||
{
|
||||
try
|
||||
{
|
||||
var field = ReflectionHelper.GetFieldInfo( instance, memberName );
|
||||
|
||||
if ( field == null )
|
||||
{
|
||||
RJLog.Log(instance, "SetValue: member not found with name ", "'" + memberName + "'" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
field.SetValue( instance, value );
|
||||
|
||||
}
|
||||
catch( System.Exception e )
|
||||
{
|
||||
RJLog.Log(e );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue