Sequence Update
This commit is contained in:
parent
a8e7988c2a
commit
a383c5da49
|
@ -23,9 +23,9 @@
|
||||||
inkscape:pagecheckerboard="0"
|
inkscape:pagecheckerboard="0"
|
||||||
inkscape:deskcolor="#505050"
|
inkscape:deskcolor="#505050"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:zoom="10.363534"
|
inkscape:zoom="20.727068"
|
||||||
inkscape:cx="-6.368484"
|
inkscape:cx="4.1491638"
|
||||||
inkscape:cy="-0.048246091"
|
inkscape:cy="6.8750679"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1017"
|
inkscape:window-height="1017"
|
||||||
inkscape:window-x="-8"
|
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
|
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") ]
|
[GlobalClass, Icon("res://Scripts/Rokojori/Rokojori-Action-Library/Icons/RJActionList.svg") ]
|
||||||
public partial class ActionList : RJAction
|
public partial class ActionList : RJAction
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** <summary for="field actions">Actions to execute</summary>*/
|
||||||
[Export]
|
[Export]
|
||||||
public RJAction[] actions;
|
public RJAction[] actions;
|
||||||
[Export]
|
|
||||||
|
/** <summary for="field triggerDirectChildren">Whether to execute RJAction child nodes</summary>*/
|
||||||
|
[Export]
|
||||||
public bool triggerDirectChildren = true;
|
public bool triggerDirectChildren = true;
|
||||||
|
|
||||||
public override void _OnTrigger()
|
public override void _OnTrigger()
|
||||||
|
@ -27,6 +46,9 @@ namespace Rokojori
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Nodes.ForEachDirectChild<RJAction>( this, a => Actions.Trigger( a ) );
|
||||||
|
|
||||||
|
/*
|
||||||
var childCount = GetChildCount();
|
var childCount = GetChildCount();
|
||||||
|
|
||||||
for ( int i = 0; i < childCount; i++ )
|
for ( int i = 0; i < childCount; i++ )
|
||||||
|
@ -41,6 +63,7 @@ namespace Rokojori
|
||||||
Actions.Trigger( action );
|
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 )
|
if ( action == null )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
action.Trigger();
|
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()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
RJLog.Log( "OnReady" );
|
// RJLog.Log( "OnReady" );
|
||||||
Actions.Trigger( action );
|
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 )
|
if ( node is T )
|
||||||
{
|
{
|
||||||
return (T)node;
|
return (T) node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
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();
|
static NodesWalker nodesWalker = new NodesWalker();
|
||||||
|
|
||||||
public static T GetAnyChild<T>( Node parent ) where T:Node
|
public static T GetAnyChild<T>( Node parent ) where T:Node
|
||||||
|
@ -63,6 +85,8 @@ namespace Rokojori
|
||||||
SetState.SetStateOfNode( NodeStateType.Disabled, n, affectProcess, affectPhysicsProcess, affectInput );
|
SetState.SetStateOfNode( NodeStateType.Disabled, n, affectProcess, affectPhysicsProcess, affectInput );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
public static void Iterate( Node[] nodes, System.Action<Node> callback )
|
public static void Iterate( Node[] nodes, System.Action<Node> callback )
|
||||||
{
|
{
|
||||||
for ( int i = 0; i < nodes.Length; i++ )
|
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 )
|
static void Stringify( object obj, StringBuilder output )
|
||||||
{
|
{
|
||||||
|
if ( obj == null )
|
||||||
|
{
|
||||||
|
output.Append( "<null>" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
output.Append( obj.ToString() );
|
output.Append( obj.ToString() );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LogMessage( string message )
|
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)
|
public static void Log( params object[] objects)
|
||||||
|
@ -36,6 +88,23 @@ namespace Rokojori
|
||||||
|
|
||||||
LogMessage( sb.ToString() );
|
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